如何统计文本中单词出现的次数

3
我正在开发一个计算机程序,用于查找文本中出现最多的前十个单词。但我遇到了瓶颈,不知道接下来该怎么办。请问有谁能帮助我吗?
目前,我的进展只到这里:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;

public class Lab4 {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner file = new Scanner(new File("text.txt")).useDelimiter("[^a-zA-Z]+");
        List<String> words = new ArrayList<String>();
        while (file.hasNext()){
            String tx = file.next();
            // String x = file.next().toLowerCase();
            words.add(tx);
        }
        Collections.sort(words);
        // System.out.println(words);
    }
}

6
一个单词列表是不够的,你还需要每个单词出现的次数计数。为了完成这样的任务,你会使用哪些数据结构?(显然,这是一道作业问题,这就是我提出这个问题的原因) 答案:你可以使用哈希表或字典来存储单词和它们的出现次数。遍历单词列表并在哈希表或字典中更新对应的单词计数即可。 - nickb
我认为你在读取文件时出现了错误。file.next() 最终会变成 null,所以你应该检查一下。 - nolegs
5个回答

10

您可以使用Guava Multiset,以下是一个单词计数的示例:http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained

以下是如何在Multiset中找到计数最高的单词:Simplest way to iterate through a Multiset in the order of element frequency?

更新:我在2012年写了这个答案。 自那时以来,我们有Java 8,现在可以在几行代码中找到使用最多的10个单词,而不需要外部库:

List<String> words = ...

// map the words to their count
Map<String, Integer> frequencyMap = words.stream()
         .collect(toMap(
                s -> s, // key is the word
                s -> 1, // value is 1
                Integer::sum)); // merge function counts the identical words

// find the top 10
List<String> top10 = words.stream()
        .sorted(comparing(frequencyMap::get).reversed()) // sort by descending frequency
        .distinct() // take only unique values
        .limit(10)   // take only the first 10
        .collect(toList()); // put it in a returned list

System.out.println("top10 = " + top10);

静态导入是:

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

因为仅仅为了完成这么简单的任务就使用一个库,所以对其进行贬低评价是过度杀鸡。 - Machinarius
2
谁说 OP 只能在这个任务中使用 Guava?对于优秀的 Java 程序员来说,Guava 就像标准集合一样,你只需要了解它。Multimap 有望被添加到 Java 8 中。 - lbalazscs
1
如果一个答案“没有用”,你应该给它点个踩,而不是因为你认为这个答案“太高级”而踩。Stackoverflow也是为了未来的参考,你不知道谁会在未来发现这个解决方案优雅且有用... - lbalazscs
每当你遇到一个极其草率、没有付出努力的帖子,或者一个明显且可能危险错误的答案时,请使用你的踩票。- 当然这里不是这种情况。http://stackoverflow.com/privileges/vote-down - lbalazscs
@lbalazscs - 干得漂亮。我怎样才能让合并和计数逻辑不区分大小写呢? - Chesser
显示剩余2条评论

4
创建一个类似下面的映射来追踪出现次数:
   Scanner file = new Scanner(new File("text.txt")).useDelimiter("[^a-zA-Z]+");
   HashMap<String, Integer> map = new HashMap<>();

   while (file.hasNext()){
        String word = file.next().toLowerCase();
        if (map.containsKey(word)) {
            map.put(word, map.get(word) + 1);
        } else {
            map.put(word, 0);
        }
    }

    ArrayList<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
    Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {

        @Override
        public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
            return a.getValue().compareTo(b.getValue());
        }
    });

    for(int i = 0; i < 10; i++){
        System.out.println(entries.get(entries.size() - i - 1).getKey());
    }

1
这里有一个比lbalazscs的版本更短的版本,也使用了Java 8的流式API;
Arrays.stream(new String(Files.readAllBytes(PATH_TO_FILE), StandardCharsets.UTF_8).split("\\W+"))
            .collect(Collectors.groupingBy(Function.<String>identity(), HashMap::new, counting()))
            .entrySet()
            .stream()
            .sorted(((o1, o2) -> o2.getValue().compareTo(o1.getValue())))
            .limit(10)
            .forEach(System.out::println);

这将一次完成所有操作:加载文件,按非单词字符分割,按单词分组并为每个组分配单词计数,然后对前十个单词打印带有计数的单词。
有关非常相似设置的深入讨论,请参见:https://dev59.com/b4Tba4cB1Zd3GeqP1iCR#33946927

为什么不使用现有的比较器.sorted(Entry.compareByValue().reversed())?(可能需要类型参数)。 - Tagir Valeev

0
package src;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Map.Entry;

public class ScannerTest
{
    public static void main(String[] args) throws FileNotFoundException
        {
        Scanner scanner = new Scanner(new File("G:/Script_nt.txt")).useDelimiter("[^a-zA-Z]+");
        Map<String, Integer> map = new HashMap<String, Integer>();
        while (scanner.hasNext())
            {
            String word = scanner.next();
            if (map.containsKey(word))
                {
                map.put(word, map.get(word)+1);
                }
            else
                {
                map.put(word, 1);
                }
            }

        List<Map.Entry<String, Integer>> entries = new ArrayList<Entry<String,Integer>>( map.entrySet());

        Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {

            @Override
            public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
                return a.getValue().compareTo(b.getValue());
            }
        });

        for(int i = 0; i < map.size(); i++){
            System.out.println(entries.get(entries.size() - i - 1).getKey()+" "+entries.get(entries.size() - i - 1).getValue());
        }
        }
}

-1
将输入作为一个字符串从文件或命令行中创建,然后将其传递给下面的方法,它将返回一个包含单词作为键和它们在该句子或段落中出现次数或计数的映射。
public Map<String,Integer> getWordsWithCount(String sentances)
{
    Map<String,Integer> wordsWithCount = new HashMap<String, Integer>();

    String[] words = sentances.split(" ");
    for (String word : words)
    {
        if(wordsWithCount.containsKey(word))
        {
            wordsWithCount.put(word, wordsWithCount.get(word)+1);
        }
        else
        {
            wordsWithCount.put(word, 1);
        }

    }

    return wordsWithCount;

}

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