Java Counting字符串中单词出现次数

时间:2010-04-14 05:25:57

标签: java regex

我正在阅读一个大文本文件,我需要找出一些单词出现的次数。例如,单词the。我逐行做这一行,每一行都是一个字符串。

我需要确保我只计算合法the - the中的other不计算在内。这意味着我知道我需要以某种方式使用正则表达式。到目前为止我尝试的是:

numSpace += line.split("[^a-z]the[^a-z]").length;  

我意识到正则表达式目前可能不正确,但我尝试了没有它,只是试图找到单词the的出现,我也得到了错误的数字。我认为这会将字符串拆分成一个数组,并且该数组被拆分的次数是字符串中单词的次数。任何想法,我将不胜感激。

更新: 鉴于一些想法,我想出了这个:

numThe += line.split("[^a-zA-Z][Tt]he[^a-zA-Z]", -1).length - 1;

虽然仍然有一些奇怪的数字。我能够获得准确的一般计数(没有正则表达式),现在我的问题是正则表达式。

8 个答案:

答案 0 :(得分:9)

使用split进行计数并不是最有效的,但如果你坚持这样做,那么正确的方法就是这样:

haystack.split(needle, -1).length -1                            

如果您未将limit设置为-1,则split默认为0,这会删除尾随的空字符串,这会影响您的计数。

来自the API

  

limit参数控制模式的应用次数,因此会影响结果数组的长度。 [...]如果n为零,则[...]尾随空字符串将被丢弃。

您还需要从数组的length中减去1,因为N出现的分隔符会将字符串拆分为N+1个部分。


对于正则表达式本身(即needle),您可以在\b周围使用word单词边界锚点。如果您允许word包含元字符(例如,计算"$US"的出现次数),您可能需要Pattern.quote


  

我想出了这个:

numThe += line.split("[^a-zA-Z][Tt]he[^a-zA-Z]", -1).length - 1;
     

虽然仍然有一些奇怪的数字。我能够获得准确的一般计数(没有正则表达式),现在我的问题是正则表达式。

现在的问题是你没有将[Tt]he计算为第一个或最后一个词,因为正则表达式说它必须在某个字符之前/之后,匹配{{1} }(也就是说,你的匹配长度必须为5!)。你不允许根本不是一个字符的情况!

您可以尝试这样的事情:

[^a-zA-Z]

这不是最简洁的解决方案,但它确实有效。

这样的事情(使用negative lookarounds)也有效:

"(^|[^a-zA-Z])[Tt]he([^a-zA-Z]|$)"

这样可以匹配只是 "(?<![a-zA-Z])[Tt]he(?![^a-zA-Z])" ,而不像之前的解决方案那样在其周围添加任何额外的字符。如果你真的想要处理由[Tt]he返回的标记,这是相关的,因为在这种情况下,分隔符不会从标记中“窃取”任何内容。


非 - split

尽管使用split来计算是相当方便的,但它并不是最有效的(例如,它会做各种工作来返回你丢弃的字符串)。事实上,正如你所说的那样逐行计数意味着该模式也必须重新编译并丢弃每一行。

更有效的方法是使用您之前执行的相同正则表达式并执行常规splitPattern.compile

答案 1 :(得分:4)

为什么不通过Java StringTokenizer运行你的行,然后你可以通过空格而不是逗号和其他标点来分解单词。只需浏览您的令牌并计算每个“the”或您想要的任何单词的出现次数。

将这一点扩展并创建一个将每个单词作为键并保持每个单词使用计数的地图将非常容易。此外,您可能需要考虑通过函数运行每个单词stem这个单词,这样您就可以计算出更有用的单词。

答案 2 :(得分:4)

分割字符串听起来像是为了找出文件中出现次数的大量开销。您可以使用String.indexOf(String, int)递归遍历整行/文件,如下所示:

int occurrences = 0;
int index = 0;
while (index < s.length() && (index = s.indexOf("the", index)) >= 0) {
    occurrences++;
    index + 3; //length of 'the'
}

答案 3 :(得分:4)

要获取特定单词的出现次数,请使用以下代码

     Pattern pattern = Pattern.compile("Thewordyouwant");
        Matcher matcher = pattern.matcher(string);
        int count = 0;
        while(matcher.find())
            count++;

答案 4 :(得分:2)

我认为这是一个单元测试真正有用的领域。前段时间我有类似的事情,我希望以多种复杂的方式打破一个字符串并创建一些测试,每个测试都针对不同的源字符串进行测试,帮助我隔离正则表达式并快速查看我弄错了。

当然,如果你给我们一个测试字符串的例子,结果会帮助我们给你更好的答案。

答案 5 :(得分:1)

您可以尝试在正则表达式中使用单词boundary \ b:

\bthe\b

同样,split返回的数组大小将比string中单词的实际出现次数多1。

答案 6 :(得分:0)

使用boyer-moore [在点击后的字符串的剩余部分]中搜索“the”并计算出现次数?

答案 7 :(得分:-2)

public class OccurenceOfWords {
 public static void main(String args[]){    
   String file = "c:\\customer1.txt";
   TreeMap <String ,Integer> index = new TreeMap();

    String []list = null;
      try(    FileReader fr = new FileReader(file);//using arm jdk 7.0 feature
                BufferedReader br = new BufferedReader(fr))
        {
            String line = br.readLine();
            while(line!= null){
                list = line.split("[ \n\t\r:;',.(){}]");
                for(int i = 0 ; i < list.length;i++)
                {
                  String word = list[i].toLowerCase();  
                    if(word.length() != 0)
                    {
                        if(index.get(word)== null)
                        { index.put(word,1);
                         }
                        else    
                        {
                            int occur = index.get(word).intValue();
                            occur++;
                            index.put(word, occur);
                        }
                        line = br.readLine();
                    }  
                }
         }}
                         catch(Exception ex){
                       System.out.println(ex.getMessage());
                       }
                    for(String item : index.keySet()){
                        int repeats = index.get(item).intValue();
                       System.out.printf("\n%10s\t%d",item,repeats);
                 }   
             }               
  }