【问题标题】:Java Counting # of occurrences of a word in a stringJava计算字符串中单词的出现次数
【发布时间】:2011-02-07 18:28:46
【问题描述】:

我有一个正在阅读的大文本文件,我需要找出一些单词出现了多少次。例如,单词the。我正在逐行执行此操作,每一行都是一个字符串。

我需要确保我只计算合法的 the's - 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;

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

【问题讨论】:

  • 我不同意你必须使用正则表达式我只是想知道你为什么使用正则表达式?
  • Though still getting some strange numbers. 听起来很有趣。你想谈谈吗?也许报告这些数字是惊人的大,还是低得离奇?

标签: java regex


【解决方案1】:

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

\bthe\b

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

【讨论】:

  • 对于split,还需要将limit设置为负数;否则它将丢弃尾随的空字符串(例如"the".split("\\bthe\\b").length == 0;如果您使用限制-1,它将按预期返回2(即比您指出的出现次数多一)。
  • 你真的不想在 Java 正则表达式中使用 \bIt just doesn’t work. :-(.
【解决方案2】:

为什么不通过 Java StringTokenizer 运行您的行,那么您不仅可以使用空格,还可以使用逗号和其他标点符号来分隔单词。只需遍历您的标记并计算每个“the”或您想要的任何单词的出现次数。

将其扩展一点并制作一个以每个单词为键并记录每个单词使用次数的映射将非常容易。此外,您可能需要考虑通过函数将每个单词运行到 stem 该单词,这样您就可以计算出比单词更有用的东西。

【讨论】:

    【解决方案3】:

    使用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,因为正则表达式表示它必须在某些字符之前/之后,与 [^a-zA-Z] 匹配(即,您的匹配长度必须为 5!)。你不允许根本没有一个字符的情况!

    你可以试试这样的:

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

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

    类似的方法(使用negative lookarounds)也可以:

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

    这具有匹配 just [Tt]he 的好处,它周围没有任何额外的字符,就像您之前的解决方案所做的那样。如果您确实想要处理由split 返回的标记,这很重要,因为在这种情况下,分隔符不会从标记中“窃取”任何内容。


    split

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

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

    【讨论】:

    • 通读文档,然后逐行吐出我的结果,看看在哪里没有看到我的搜索词:“[^a-zA-Z][Tt]he[^a- zA-Z]" 不计算从字符串开头开始的任何 'the'。有什么原因吗?
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      为了找出文件中出现的次数,拆分字符串听起来像是很多开销。您可以使用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'
      }
      

      【讨论】:

      • 除非您在循环体中添加 index++,否则它将永远循环。
      • @mchr 对!基本错误..也许它现在可以更正确地工作
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:
        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);
                         }   
                     }               
          }
        

        【讨论】:

        • 迄今为止最长的解决方案。您介意解释一下与其他解决方案有什么区别吗?为什么您认为您的解决方案在 2 年后如此重要?
        • 我是 stackoverflow 的忠实粉丝,我的一个对 java 不熟悉的朋友向我询问了一个使用 java 计算文本文件中单词出现次数的解决方案,我欣然建议他使用 stackoverflow说这些信息不足以让他建立自己的程序并寻求帮助
        【解决方案8】:

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

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

        【讨论】:

        • 是否有一个衬垫用于替换 while(matcher.find()) count++; ?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-14
        • 2021-01-09
        • 1970-01-01
        • 2013-02-01
        • 2019-07-31
        • 2014-04-29
        • 1970-01-01
        相关资源
        最近更新 更多