英文文章分词及已知单词位置计算单词在文章中起始下标

###英文文章分词及已知单词位置计算单词在文章中起始下标
####背景
1.由于最近项目中需要,要实现类似文章跟读的效果,但已知的只有每个单词在文章中的位置下标(即每个单词在文章中是在第几个单词),那么要实现跟读效果就必须根据每个单词在文章中的位置计算出每个单词在整个文章中的具体下标。比如:“My name is Tom.”,我们只知道“My” 、“name”、“is”、“Tom”在文章中分别是第0、1、2、3个单词,而要实现跟读则必须计算出每个词具体的起始下标,如“My” 、“name”、“is”、“Tom”每个词在文章中的起始下标分别为0、3、8、11。这样才能用spanableString实现跟读效果(用法在下一篇博客讲,这里只关注分词相关)

####实现
1.分词

  • 首先将分别信息进行分词。分词的步骤是先将文本中的多余空格和换行符、制表符等去掉,只保留每个单词(带符号)之间的一个空格,然后根据空格将文本进行分词。(当然项目中的文章资源需要配合,保证每个词/词+符号 间有空格)这里故意保留符号,符号是算在前一个词里面的,因为跟读标记需要把标点也标记进去。
public String[] divideToWords(String text) {
    String[] words;
    String str = text.replaceAll("\\s+|\t|\n|\r" +
            "|\n\n", " ");
    str = str.trim();  //去掉收尾空格
    words = str.split(" ");
    return words;
}

2.计算每个单词在文章中的下标

  • 实现过程需要注意的点:某个单词可能在文章中出现多次;某些单词是缩写的形式,中间可能存在一些字符而非纯字母组成(I’m、don’t等)。
  • 实现步骤:先计算每个词在文章中出现次数,用hashMap存储,key为单词,value为出现次数,同时用另一个hashMap存储第n个单词的信息(只要关注第n个单词在文章中是第几次出现);然后遍历每个单词,计算每个单词在文章中的开始下标。
  • 匹配单词的规则是前面是非英文字符,后面是空格、回车等(文章最后如果没有,要先补上,否则最后一个可能匹配不上);用正则匹配时要注意先转义* ?等特殊字符,因为这些字符在正则表达式中有特殊的意义和作用。
 public HashMap<Integer, WordEntity> calculateTextStartIndex(String[] words, String text) {

        wordCountMap.clear();
        wordEntitiesMap.clear();

        for (int i = 0; i < words.length; i++) {
            AppsLog.i(TAG, "单词" + i + " : " + words[i]);

            int repeatCount = 1;
            if (wordCountMap.get(words[i]) != null) {
                repeatCount = wordCountMap.get(words[i]) + 1;
            }
            wordCountMap.put(words[i], repeatCount); //记录单词出现次数(第几次出现)

            WordEntity entity = new WordEntity();
            entity.setUnit_index(i);
            entity.setWord(words[i]);
            entity.setRepeatCount(repeatCount); //第几次出现
            wordEntitiesMap.put(i, entity); //记录单词信息
        }

        //循环遍历查找每个单词的下标
        for (int i = 0; i < wordEntitiesMap.size(); i++) {
            WordEntity entity = wordEntitiesMap.get(i);
            int repeatCount = entity.getRepeatCount();
            String word = entity.getWord();

            int totalIndex = -1;
            String temp = text;
            for (int j = 0; j < repeatCount; j++) {
                int index = -1;
                if (i == 0) {
                    //如果是第一个单词,直接用indexOf
                    index = temp.indexOf(word);
                    if (index != -1) {
                        AppsLog.i(TAG, "Found1 : " + word + "  at  " + index);
                    }
                } else {
                    //非第一个单词,需要用正则不断匹配取得下标,同时得跳过前面已经重复的单词(检测要匹配的单词,单词的前面必须是空格或者标点符号)
                    String patternWord = word.replace(".", "\\.")   //转义替换正则的特殊字符
                            .replace("?", "\\?")
                            .replace("*", "\\*")
                            .replace("+", "\\+")
                            .replace("^", "\\^")
                            .replace("(", "\\(")
                            .replace(")", "\\)")
                            .replace("$", "\\$")
                            .replace("[", "\\[")
                            .replace("]", "\\]")
                            .replace("{", "\\{")
                            .replace("}", "\\}")
                            .replace("|", "\\|");
                    //.replace("\\", "\\\\") 
                    //前面必须非英文字母,后面为空格或回车等(I'm)
                    Pattern p = Pattern.compile("[^\\w]" + patternWord + "[\\s\\r\\n]"); 
                    Matcher m = p.matcher(temp);
                    if (m.find()) {
                        index = m.start() + 1;   //起始下标
                        AppsLog.i(TAG, "Found2 : " + word + "  at  " + index);
                    }
                }

                if (index != -1) {  //找到
                    temp = temp.substring(index + word.length());  //去掉已匹配过的前面的文本
                    if (totalIndex == -1) {
                        totalIndex = 0;
                    }
                    totalIndex += (index + word.length());  //算几次index的总和加词的长度
                    if (totalIndex != 0) {
                        if (j == (repeatCount - 1)) {     //最后一次匹配,index减去单词长度
                            totalIndex -= word.length();
                        }
                    }
                } else {
                    if (totalIndex == -1) {
                        totalIndex = 0;
                    }
                    if (totalIndex != 0) {
                        if (j == (repeatCount - 1)) {
                            totalIndex -= word.length();
                        }
                    }
                }
            }
            if (totalIndex != -1) {
                entity.setStartPos(totalIndex);
            }
            wordEntitiesMap.put(i, entity);
        }
        for (int i = 0; i < wordEntitiesMap.size(); i++) {
            AppsLog.i(TAG, "" + i + " : " + wordEntitiesMap.get(i));
        }

        return wordEntitiesMap;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vinson武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值