Java计算字符串中单词出现次数

5

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

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

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

我知道正则表达式可能目前不正确,但我试过了没有它,并尝试查找单词the的出现次数,我得到的数字也是错误的。我原本以为这会将字符串拆分成数组,而拆分数组的次数就是单词在字符串中出现的次数。有什么想法,我将不胜感激。

更新: 经过一些想法,我想出了这个:

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

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


我不同意你必须使用正则表达式,我只是想知道为什么你要使用正则表达式? - Jeff Beck
尽管仍然得到一些奇怪的数字,听起来很有趣。你想谈论一下吗?也许报告一下那些数字是惊人的大还是好奇的低? - user unknown
8个回答

9

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

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

如果您没有将limit设置为-1,则split默认为0,这会移除尾随空字符串,从而使您的计数出错。
API中可以看出:

limit参数控制模式应用的次数,从而影响生成的数组长度。如果n为零,则丢弃尾随的空字符串。

您还需要从数组的length中减去1,因为定界符的N个出现将字符串分成N+1部分。
关于正则表达式本身(即needle),您可以在word周围使用单词边界锚点\b。如果您允许word包含元字符(例如计算"$US"的出现次数),则可能要使用Pattern.quote

I've come up with this:

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

Though still getting some strange numbers. I was able to get an accurate general count (without the regular expression), now my issue is with the regexp.

现在问题是你没有计算出现在第一个或最后一个单词的“the”,因为正则表达式指定它必须在某个字符前面/后面,匹配[^a-zA-Z](也就是说,你的匹配必须长度为5!)你没有考虑根本没有字符的情况!
你可以尝试使用以下内容:
"(^|[^a-zA-Z])[Tt]he([^a-zA-Z]|$)"

这不是最简洁的解决方案,但它可以工作。

使用负向环视(negative lookarounds)也可以像下面这样实现:

"(?<![a-zA-Z])[Tt]he(?![^a-zA-Z])"

这样做的好处是只匹配仅有的[Tt]he,没有像之前的解决方案一样在它周围添加任何额外的字符。如果你实际上想要处理split返回的标记,这点非常重要,因为在这种情况下分隔符并不会从标记中“窃取”任何信息。

split

虽然使用split来计数相当方便,但它并不是最有效的方法(例如,它正在执行各种工作以返回那些你丢弃的字符串)。正如你所说,你是逐行计数的,这意味着模式也必须在每行重新编译和丢弃。

更有效的方法是使用与之前相同的正则表达式,并进行通常的Pattern.compilewhile (matcher.find()) count++;操作。


阅读文档,然后逐行输出我的结果,以查看它在哪里没有看到我的单词,我的搜索是: "[^a-zA-Z][Tt]he[^a-zA-Z]" 不计算任何从字符串开头开始的“the”。这是为什么呢? - Doug

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

使用下面的代码来获取特定字词的出现次数

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

有没有一行代码可以替换 while(matcher.find()) count++;? - Jenna Kwon

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'
}

2
除非在循环体内添加index++,否则此循环将永远执行。 - mchr
@mchr 没错!基本错误..也许现在它的工作更正确了。 - fish

4

为什么不通过Java StringTokenizer 运行您的代码,这样您就可以将单词分解成不仅是空格,还有逗号和其他标点符号。只需运行您的令牌并计算每个“the”或任何您想要的单词的出现次数即可。

稍微扩展一下就可以很容易地创建一个以每个单词为键并保持每个单词使用计数的映射。此外,您可能需要考虑将每个单词通过函数stem处理,以便您可以计算比单词更有用的东西。


2
我认为这是单元测试可以真正帮助的领域。我曾经有过类似的经历,当时我想以多种复杂的方式拆分字符串并创建多个测试,每个测试针对不同的源字符串进行测试,这有助于我隔离正则表达式,并在我做错时快速发现。

如果您给我们提供一个测试字符串和结果的示例,那么我们可以更好地回答您的问题。


1
你可以尝试在正则表达式中使用单词边界符\b:
\bthe\b

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


1
你还需要将 limit 设置为负数以进行 split;否则它会丢弃尾随的空字符串(例如 "the".split("\\bthe\\b").length == 0;如果你使用 limit -1,它将返回预期的 2(即比你指出的出现次数多一个)。 - polygenelubricants
你真的不想在Java正则表达式中使用\b。它根本不起作用。 :-( - tchrist

0
使用Boyer-Moore算法在字符串的剩余部分中搜索“the”,并计算出现次数?

-1
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);
                 }   
             }               
  }

1
到目前为止最长的解决方案。您介意解释一下与其他解决方案的区别,为什么您认为您的解决方案在两年后如此重要吗? - user unknown
2
我是StackOverflow和编程的忠实粉丝。我的一个朋友刚接触Java,他问我如何使用Java统计文本文件中单词出现的次数。我毫不犹豫地建议他去StackOverflow寻找答案。但他说那里的信息对他来说不够充分,无法帮助他自己编写程序,于是他向我求助。我也有同感,所以给了他一个例子,并分享给了其他像他一样的人。 - narendra kumar botta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接