今天看到关于移到关于字符串查找和替换的题目, 如下:

现有2个文件,地址在:

http://***.com/sites/task3.properties

http://***.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<Long> {
    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

posted on 2013-10-12 14:02  ZimZz  阅读(2414)  评论(0编辑  收藏  举报