【问题标题】:Java: counting occurence of words, program counts 'empty' wordsJava:计算单词的出现次数,程序计算“空”单词
【发布时间】:2020-03-13 19:20:38
【问题描述】:

我有一个程序,它从文本文件中获取输入,删除标点符号,然后按单个空格分割并将结果汇​​总到地图中。我可以让它工作,但我在地图上也得到了一个空的结果,我不知道给出了什么:

扫描仪接受输入:

try
        {
            Scanner input = new Scanner(file);
            String nextLine;
            while (input.hasNextLine())
            {
                nextLine = input.nextLine().trim();
                processLine(nextLine, occurrenceMap);
            }
            input.close();
        }
        catch(Exception e) { System.out.println("Something has gone wrong!");}

从中提取的文本文件是詹姆斯国王版本的圣经 然后一个单独的函数处理每一行:

//String[] words = line.replaceAll("[^a-zA-Z0-9 ]", " ").toLowerCase().split("\\s+"); // runtime for  bible.txt is ~1600ms

// changed to simple iteration and the program ran MUCH faster:

char[] letters = line.trim().toCharArray();
for (int i=0; i<letters.length; i++)
{
    if (Character.isLetterOrDigit(letters[i])) {continue;}
    else {letters[i] = ' ';}
}

String punctuationFree = new String(letters);
String[] words = punctuationFree.toLowerCase().split("\\W+");

// add each word to the frequency map:
for (int i=0; i<words.length; i++)
{
    if (! map.containsKey(words[i]))
    {
        map.put(words[i], 1);
    }
    else
    {
        int value = (int)map.get(words[i]);
        map.put(words[i], ++value);
    }
}

如您所见,我首先使用全部替换,然后我想出了我自己的时髦迭代方法(它似乎运行得更快)。在这两种情况下,当我使用 PrintWriter 打印结果时,我都会在开头得到一个奇怪的条目:

num occurences/ (number /word)

25307 :     // what is up with this empty value ?
1 : 000     // the results continue in sorted order
2830 : 1
2122 : 10
6 : 100
9 : 101
29 : 102
23 : 103
36 : 104
46 : 105
49 : 106

我尝试将 String[] words = punctuationFree.toLowerCase().split("\\W+"); 更改为 .split("\s+") 和 .split(" ") 但我仍然在结果中得到这个空值。

我试图只计算单词和数字的出现次数,为什么我得到这个空值?

更新:在 Character.isLetterOrDigit() 可能返回不需要的字符的建议下,我重写了检查,以便只获取我想要的字符。尽管如此,我仍然得到一个神秘的空值:

for (int i=0; i<letters.length; i++)
    {
        if ((letters[i] >= 'a' && letters[i] <= 'z') || 
           (letters[i] >= 'A' && letters[i] <= 'Z'))
           {continue;}
        else if (letters[i] >= '0' && letters[i] <= '9')
           {continue;}
        else if ((letters[i] == ' ')||(letters[i] =='\n')||(letters[i] == '\t'))
           {continue;}
        else
            letters[i] = ' ';
    }

【问题讨论】:

  • 了解line 中的内容会有所帮助。
  • 我会更新问题...
  • 了解line 中的内容仍然会有所帮助,而不仅仅是对其中内容的描述。

标签: java regex dictionary split punctuation


【解决方案1】:

只是猜测,但 Character 方法 IsLetterOrDigit 被定义为适用于整个 unicode 范围。根据文档page,它包括所有“有效字母和十进制数字是 UnicodeCategory 中以下类别的成员:UppercaseLetter、LowercaseLetter、TitlecaseLetter、ModifierLetter、OtherLetter 或 DecimalDigitNumber。”

我认为这种方法会保留您不想要的字符(特别是 ModifierLetter 和/或 OtherLetter),并且它们未包含在您的字体中,因此您看不到它们。

编辑 1: 我测试了你的算法。事实证明,空行绕过了您的测试,因为它跳过了 for 循环。您需要在从文件中读取一行后添加一个行长:

if (nextLine.length() == 0) {continue;}

编辑 2:此外,由于您正在扫描每个字符以清除“非单词和非数字”,您还可以合并创建单词的逻辑并将它们添加到收藏。可能是这样的:

private static void WordSplitTest(String line) {
    char[] letters = line.trim().toCharArray();

    boolean gotWord = false;

    String word = "";

    for (int i = 0; i < letters.length; i++) {
        if (!Character.isLetterOrDigit(letters[i])) {

            if(!gotWord) {continue;}

            gotWord = false;

            AddWord(word);
        }
        if (gotWord) {
            word += Character.toString(letters[i]);
        }
    }
}

private static void AddWord(String word) {
    if (!map.containsKey(word)) {
        map.put(word, 1);
    } else {
        int value = (int) map.get(word);
        map.put(word, ++value);
    }
}

【讨论】:

  • 我重做了字符检查,只使用 检查 ASCII,我仍然在输出中得到相同的结果
  • 非常感谢!我没有想到空行。我使用非常相似的算法让它在 Python 中工作,但我猜空行没有在 Java 中注册。
  • 所以我把if (nextLine.length() == 0) {continue;}放在处理每个字符的步骤中,它仍然让35个空字通过。所以我听从了你的建议,当这些词被添加到地图上时,我也做了同样的检查,就像口袋妖怪:我全都知道了。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-09
  • 2021-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-25
相关资源
最近更新 更多