今天看到关于移到关于字符串查找和替换的题目, 如下:
现有2个文件,地址在:http://***.com/sites/task3.propertieshttp://***.com/sites/task3.txt要求:根据properties中内容替换掉txt里$function(index)形式文字,将其还原成一本完整小说。其中function有4种函数,替换规则如下:1、natureOrder 自然排序,即文本中排列顺序2、indexOrder 索引排序,文本中每行第一个数字为索引3、charOrder 文本排序(Java默认的Unicode排序),java的字符排序4、charOrderDESC 文本倒序,java的字符倒序注意:txt和properties文件需要程序中现读现解析,而不是下载成本地文件。5、通过执行com.qunar.fresh.task.checks.Check2 查看自己程序结果,格式如下:name true time。第二列true表示结果正确,false结果不正确,第三列time是执行时间,毫秒为单位。6、代码提交的地址是:****大家在这个目录下创建自己的代码,包名是大家的rtx名字不带点。可以参照example,大家的代码写到Task2里,当然可以创建新的其他文件进行封装。7、尽量写下单元测试,注意编码规范。
其中 task3.txt 结构如下
第一回 风月无情“越女采莲秋水畔,窄袖轻罗,暗露双金钏。照影摘花花似面,芳心只共丝争乱。鸡尺溪头风浪晚,雾重烟轻,不见来时伴。隐隐歌声归棹远,离愁引着江南岸。”一阵轻柔婉转的歌声,飘在烟水蒙蒙的湖面上。歌声发自一艘小船之中,船里五个少女和歌嬉笑,荡舟采莲。她们唱的曲子是北宋大$natureOrder(0)“蝶恋花”词,写的正是越女采莲的情景,虽只寥寥六十字,但季节、时辰、所在、景物以及越女的容貌、衣着、首饰、心情,无一不描绘得历历如见,下半阕更是写景中有叙事,叙事中夹抒情,自近而远,余意不尽。欧阳修在江南为官日久,吴山越水,柔情蜜意,尽皆融入长$charOrder(4641)。宋人不论达官贵人,或是里巷小民,无不以唱词为乐,是以柳永新词一出,有井水处皆歌,而江南春岸折柳,秋湖采莲,随伴的往往便是欧词。时当南宋理宗年间,地处嘉兴南湖。节近中秋,荷叶渐残,莲肉饱实。这一阵歌声传入湖边一个道姑耳中。她在一排柳树下悄立已久,晚风拂动她杏黄色道袍的下摆,拂动她颈中所插拂尘的万缕柔丝,心头思潮起伏,当真亦是“芳心只共丝争乱”。只$charOrderDESC(4177)声渐渐远去,唱的是欧阳修另一首“蝶恋花”词,一阵风吹来,隐隐送来两句:“风月无情人暗换,旧游如梦空肠断……”歌声甫歇,便是一阵格格娇笑。那道姑一声长叹,提起左手,瞧着染满了鲜血的手掌,喃喃自语:“那又有甚么好笑?小妮子只是瞎唱,浑不解词中相思之苦、惆怅之意。”在那道姑身后十余丈处,一个青袍长须的老者也是一直悄立不动。只有当“风月无情人暗换,旧游如梦空肠断”那两句传到之时,发出一声极轻极轻的叹息。小船在碧琉璃般的湖面上滑过,舟中五个$indexOrder(5722)五六岁上下,另外两个都只九岁。两个幼女是中表之亲,表姊姓程,单名一个英字,表妹姓陆,名无双。两人相差半岁。三个年长少女唱着歌儿,将小舟从荷叶丛中荡将出来。程英道:“表妹你瞧,这位老伯伯还在这儿。”说着伸手指向垂柳下的一人。
task3.properties文件如下:
1755 词人欧阳修所作的3217 短句中4378 听得歌5722 少女中三人十1692 了老半天啦3360 里取出一3723 你不来4832 们大了好几岁2310 陆无双只得松5835 你玩啦5337 光之中2591 自禁的4798 色俱厉2680 在十年前就是5503 程英的手转身便3157 还不如让927 一滴眼泪也324 哭得心酸2110 记着这个新相5546 丑陋的脸上露出了1197 公给糖糖3958 下赫然并641 言自语3960 可惜我802 要接她回大理4475 个死大伯的老
题目大意是根据不同的要求将文本中的模板字符替换为properties文件定义的原文.
程序如下
public class Task2 implements Task{ private static final String NATURAL_ORDER = "natureOrder"; private static final String INDEX_ORDER = "indexOrder"; private static final String CHAR_ORDER = "charOrder"; // private static final String CHAR_ORDER_DESC = "charOrderDESC"; private static final int READ_BUFF_SIZE = 512; private static final int WRITE_BUFF_SIZE = 1024; private static final int ARRAY_SIZE = 6110; /** 原小说Reader, 已封装为CharArrayReader */ private final Reader templateReader; /** 替换词组资源文件Reader, 已封装为CharArrayReader */ private final Reader configReader; /** 替换完成文件Writer, 已封装为FileWriter */ private final Writer mergedWriter; /** 自然顺序替换集 */ private String[] naturalOrderRep = new String[ARRAY_SIZE]; /** 索引顺序词条集 */ private String[] indexOrderRep = new String[ARRAY_SIZE]; /** 字符顺序词条集 */ private String[] charOrderRep = new String[ARRAY_SIZE]; Task2(Reader templateReader, Reader configReader, Writer mergedWriter) { this.templateReader = templateReader; this.configReader = configReader; this.mergedWriter = mergedWriter; } @Override public Long execute() { try { initOrderRep(); // 开始替换数据 BufferedReader templateBuffReader = new BufferedReader(templateReader, READ_BUFF_SIZE); BufferedWriter mergedBuffWriter = new BufferedWriter(mergedWriter, WRITE_BUFF_SIZE); String s = templateBuffReader.readLine(); while (s != null) { mergedBuffWriter.write(replaceWord(s)); s = templateBuffReader.readLine(); if (s != null) mergedBuffWriter.write("\n"); } mergedBuffWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { templateReader.close(); configReader.close(); mergedWriter.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 替换单词方法, 此方法假定每一行只有一个需要替换的词组 * * @param originalSentence 待替换的原始句子 * @return 替换完成的句子 * @throws IOException */ private String replaceWord(String originalSentence) throws IOException { int dollar = originalSentence.indexOf('$'); // 存在可替换字串 if (dollar != -1) { int leftParenthese = -1; int rightParenthese = -1; // 获取左右括弧坐标 for (int i = dollar; i < originalSentence.length(); i++) { if (originalSentence.charAt(i) == '(') { leftParenthese = i; } else if (originalSentence.charAt(i) == ')') { rightParenthese = i; break; } } int numStart = leftParenthese + 1; // 编号开始位置 int numEnd = rightParenthese - 1; // 编号结束位置 int typeStart = dollar + 1; // 替换类型开始位置 int typeEnd = leftParenthese - 1; // 替换类型结束位置 int num = Integer.valueOf(originalSentence.substring(numStart, numEnd + 1)); // 替换单词编号 String type = originalSentence.substring(typeStart, typeEnd + 1); // 替换单词类型 String target = originalSentence.substring(dollar, rightParenthese + 1); // 待替换字符串 if (type.equals(NATURAL_ORDER)) { originalSentence = originalSentence.replace(target, naturalOrderRep[num]); } else if (type.equals(INDEX_ORDER)) { originalSentence = originalSentence.replace(target, indexOrderRep[num]); } else if (type.equals(CHAR_ORDER)) { originalSentence = originalSentence.replace(target, charOrderRep[num]); } else { originalSentence = originalSentence.replace(target, charOrderRep[charOrderRep.length - num - 1]); } } return originalSentence; } /** * 准备替换数据 * * @throws IOException */ private void initOrderRep() throws IOException { BufferedReader configBuffReader = new BufferedReader(configReader, READ_BUFF_SIZE); String s; int index = 0; while ((s = configBuffReader.readLine()) != null) { int tabIndex = s.indexOf('\t'); String word = s.substring(tabIndex + 1, s.length()); Integer wordIndex = Integer.parseInt(s.substring(0, tabIndex)); naturalOrderRep[index] = word; indexOrderRep[wordIndex] = word; index++; } System.arraycopy(naturalOrderRep, 0, charOrderRep, 0, naturalOrderRep.length); Arrays.sort(charOrderRep); }}
这是主要的实现替换功能的代码, 其中
1. 小说文件内容已经被读到char[]中, 读取小说内容的Reader为一个CharArrayReader templateReader, 封装过程程序中并没有给出
2. properties文件已经被读到char[]中, 读取properties的Reader为一个CharArrayReader configReader, 封装过程程序中并没有给出
3. 写入新文件的writer为FileWriter mergedWriter, 封装过程程序没有给出
细节优化点:
1. 程序建立在基于对待处理文本有充分了解的情况下, 可知文本每行只有一个需要替换的模板, 待替换资源文件词条数为6110, 所以不需要做过多复杂情况的考虑
2. 将所有等待替换的资源读取为字符串数组, 原始文件顺序资源数组为naturalOrderRep, 字符顺序数组为 charOrderRep, 索引顺序数组为 indexOrderRep
3. 进行短路操作, 如只有在每行发现美元符才继续向前处理字符 ,
String replaceWord(String originalSentence) 方法
int dollar = originalSentence.indexOf('$'); // 存在可替换字串 if (dollar != -1) {
4. 每行小说数据只顺序遍历一遍,在遍历过程中保存所有需要的坐标值, 避免重复使用indexOf, 如
int dollar = originalSentence.indexOf('$'); // 存在可替换字串 if (dollar != -1) { int leftParenthese = -1; int rightParenthese = -1; // 获取左右括弧坐标 for (int i = dollar; i < originalSentence.length(); i++) { if (originalSentence.charAt(i) == '(') { leftParenthese = i; } else if (originalSentence.charAt(i) == ')') { rightParenthese = i; break; } }
5. 选择合适的Buffer来read和write