Java文本处理:高效移除停用词与词频统计

发布时间:2026/6/11 14:55:44

Java文本处理:高效移除停用词与词频统计 本教程旨在指导读者如何使用Java有效地从文本文件中删除指定的停止单词并进一步统计清洗后文本中单词的频率。通过结合Java NIO.2.文件操作、字符串处理和集合框架我们将构建一个强有力的解决方案以实现文本数据的预处理和基本分析。处理自然语言NLP在该领域文本预处理是删除停用词的关键步骤Stop Words这是一个常见的操作。停用词通常是指那些经常出现在文本中但对文本语义贡献不大的词如“是”、“是”、“在”等等。删除这些词有助于降低数据维度提高后续文本分析如词频统计和文本分类的效率和准确性。本教程将详细介绍如何使用Java来实现这一过程并在此基础上进行词频统计。核心概念与技术栈文件I/O操作 Java NIO.2 (java.nio.file.Files 和 java.nio.file.Paths)与传统文件相比它提供了更现代、更高效的文件读写API FileInputStream 和 BufferedReader 更为简洁。字符串处理 利用 String 类的 split() 文本分割的方法以及 replaceAll() 停用词替换的方法。集合框架 使用 java.util.Map (具体为 HashMap) 存储词汇及其频率并通过 java.util.List 和 Collections.sort() 对词频进行排序。正则表达式 合理使用正则表达式可以在停词替换和词汇分割时实现更精确的匹配和处理。实现步骤我们将逐步实现首先读取主文本文件和停用词文件然后从主文本中删除停用词最后统计剩余词汇的频率。1. 阅读文件内容首先我们需要这样做 hello.txt(主文本)和 stopwords.txt读取内存中的内容(停用词列表)。Files.readString() 该方法是阅读小到中文件内容的理想选择。import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; public class TextProcessor { public static void main(String[] args) { try { // 阅读主文本文件的内容 String mainTextContent Files.readString(Paths.get(hello.txt), Charset.defaultCharset()); // 读取停用词文件的内容 String stopWordsContent Files.readString(Paths.get(stopwords.txt), Charset.defaultCharset()); System.out.println(原文内容 mainTextContent); System.out.println(停用词内容 stopWordsContent); // ... 后续处理 } catch (IOException e) { System.err.println(文件读取错误 e.getMessage()); e.printStackTrace(); } } }示例文件内容hello.txt: remove leave remove leave remove leave re move remov e leave remove hello remove world!stopwords.txt: remove world2. 移除停用词读完停用词后我们需要从主文本内容中删除它们。在这里我们将停用词文件的内容根据空白字符分成单独的单词然后通过这些单词使用 String.replaceAll() 从主文本中替换它们的方法。为了确保只替换完整的单词而不是单词的一部分我们将结合正则表达式的单词边界 。// ... (连接上一步代码) String[] stopWordsArray stopWordsContent.split(\s); // 使用 \s 匹配一个或多个空白字符 String cleanedText mainTextContent; for (String stopWord : stopWordsArray) { // 构建正则表达式使用 确保与整个单词相匹配 // 例如如果 stopWord 是 remove则匹配 remove // 忽略大小写处理可能的标点符号 cleanedText cleanedText.replaceAll((?i)\b stopWord \b, ); } // 清理多余的空白字符用单个空间替换多个空间清除首尾空间 cleanedText cleanedText.replaceAll(\s, ).trim(); System.out.println( 清洗后的文本内容 cleanedText); // ...注意事项split(\s) 用于将停用词字符串分成数组\s 匹配一个或多个空白字符。replaceAll((?i)\b stopWord \b, ) 是关键。(?i) 是嵌入式标志表达式表示匹配不区分大小写。它是单词的边界以确保我们只替换完整的单词而不是单词的一部分(例如避免使用它们。 remove 从 remover 中移除。用空字符串代替 。最后的 replaceAll(\s, ).trim() 将清洗后可能产生的多个连续空间合并成一个去除文本首尾的空间使文本更加整洁。3. 词频统计与排序清洗后的文本现在只包含有意义的单词。接下来我们将统计这些单词的频率并根据频率进行排序以找出最常见的单词。// ... (连接上一步代码) // 将清洗后的文本分成单词 // 使用正则表达式匹配非字母数字符作为分隔符并将其转换为小写 String[] words cleanedText.toLowerCase().split([^a-zA-Z0-9]); // 使用 HashMap 存储词频 MapString, Long wordFrequencies Arrays.stream(words) .filter(word - !word.isEmpty()) // 过滤空字符串 .collect(Collectors.groupingBy( String::valueOf, // 词汇本身就是键 Collectors.counting() // 统计出现次数 )); // 将词频Map转换为List以便排序 // 使用LinkedHashmap保持插入顺序(排序后重建时有用) MapString, Long sortedWordFrequencies wordFrequencies.entrySet() .stream() .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 按值降序排序 .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) - oldValue, // 合并函数这里不应该发生键冲突 LinkedHashMap::new // 保持排序顺序 )); System.out.println( 词频统计 (降序):); sortedWordFrequencies.forEach((word, count) - System.out.println(word : count)); // 如果需要显示Top N词汇 int topN 3; // 例如显示前3个 System.out.println( Top topN 词汇:); sortedWordFrequencies.entrySet() .stream() .limit(topN) .forEach(entry - System.out.println(entry.getKey() : entry.getValue())); // ...解释cleanedText.toLowerCase().split([^a-zA-Z0-9])toLowerCase()将所有单词转换为小写以确保“Hello”和“hello被视为同一个词。split([^a-zA-Z0-9])使用一个或多个非字母数字符作为分隔符来分割文本这有助于删除标点符号并获得纯单词。Arrays.stream(words).filter(word - !word.isEmpty()).collect(Collectors.groupingBy(String::valueOf, Collectors.counting()))这是一款高效的Java 8 Stream API用法。filter(word - !word.isEmpty())过滤掉分割可能产生的空字符串。Collectors.groupingBy(String::valueOf, Collectors.counting())根据其值(单词本身)对Stream中的元素进行分组并计算每组(单词)的出现次数。wordFrequencies.entrySet().stream().sorted(...).collect(...)将 HashMap 的 entrySet() 转换为Stream。sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))根据Map的值(即词频)进行降序排序。Collectors.toMap(...)将Stream重新收集到新的Stream LinkedHashMap 保持排序顺序。4. 将结果写入文件(可选)如果需要将清洗后的文本或词频统计结果保存到文件中可以使用 Files.write() 方法。// ... (连接上一步代码) // 将清洗后的文本写入新文件 // Files.write(Paths.get(cleaned_hello.txt), cleanedText.getBytes(Charset.defaultCharset())); // System.out.println( 清洗后的文本已保存到 cleaned_hello.txt); // 将词频统计结果写入文件 // StringBuilder freqOutput new StringBuilder(); // sortedWordFrequencies.forEach((word, count) - freqOutput.append(word).append(: ).append(count).append( )); // Files.write(Paths.get(word_frequencies.txt), freqOutput.toString().getBytes(Charset.defaultCharset())); // System.out.println(词频统计结果已保存 word_frequencies.txt); // ...完整的代码示例import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; public class TextProcessor { public static void main(String[] args) { try { // 1. 阅读文件内容 String mainTextContent Files.readString(Paths.get(hello.txt), Charset.defaultCharset()); String stopWordsContent Files.readString(Paths.get(stopwords.txt), Charset.defaultCharset()); System.out.println(--- 原始数据 ---); System.out.println(原文内容 mainTextContent); System.out.println(停用词内容 stopWordsContent); // 2. 移除停用词 String[] stopWordsArray stopWordsContent.split(\s); String cleanedText mainTextContent; for (String stopWord : stopWordsArray) { // 使用 确保整个单词匹配(?i) 忽略大小写 cleanedText cleanedText.replaceAll((?i)\b stopWord \b, ); } // 清理多余的空白字符 cleanedText cleanedText.replaceAll(\s, ).trim(); System.out.println( --- 文本清洗结果 ---); System.out.println(清洗后的文本内容 cleanedText); // 3. 词频统计与排序 // 将清洗后的文本分成单词转化为小写并过滤空字符串 String[] words cleanedText.toLowerCase().split([^a-zA-Z0-9]); MapString, Long wordFrequencies Arrays.stream(words) .filter(word - !word.isEmpty()) .collect(Collectors.groupingBy( String::valueOf, Collectors.counting() )); // 按词频降序排序 MapString, Long sortedWordFrequencies wordFrequencies.entrySet() .stream() .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) - oldValue, LinkedHashMap::new )); System.out.println( --- 词频统计 (降序) ---); sortedWordFrequencies.forEach((word, count) - System.out.println(word : count)); // 4. 显示Top N词汇 (例如 Top 3) int topN 3; System.out.println( --- Top topN 词汇 ---); sortedWordFrequencies.entrySet() .stream() .limit(topN) .forEach(entry - System.out.println(entry.getKey() : entry.getValue())); // 可选将清洗后的文本写入文件中 // Files.write(Paths.get(cleaned_hello.txt), cleanedText.getBytes(Charset.defaultCharset())); // System.out.println( 清理后的文本已保存到保存中 cleaned_hello.txt); } catch (IOException e) { System.err.println(文件操作错误 e.getMessage()); e.printStackTrace(); } } }如果操作上述代码 hello.txt 和 stopwords.txt 存在于项目根目录中类似于以下输出--- 原始数据 --- 原文内容 remove leave remove leave remove leave re move remov e leave remove hello remove world! 停用词内容 remove world --- 文本清洗结果 --- 清洗后的文本内容 leave leave leave re move remov e leave hello ! --- 词频统计 (降序) --- leave: 4 re: 1 move: 1 remov: 1 e: 1 hello: 1 --- Top 3 词汇 --- leave: 4 re: 1 move: 1注意事项和优化文件编码 始终明确指定文件编码如 Charset.defaultCharset() 或 StandardCharsets.UTF_8避免乱码问题。大文件处理 Files.readString() 适用于中小型文件。建议使用GB级大文件 Files.lines() 逐行处理或结合 BufferedReader 避免一次性将所有内容加载到内存中以防止 OutOfMemoryError。正则表达式的准确性单词边界非常重要可以防止意外替换。例如如果没有 移除“cat“”可能会影响“”category”。对于更复杂的停用词(如包含特殊字符)可能需要对停用词本身进行正则转义:Pattern.quote(stopWord)。性能优化String.replaceAll() 会创建新的字符串对象在循环中频繁调用可能效率不高。可以考虑极端性能要求 StringBuilder 结合 Matcher.appendReplacement() 和 appendTail() 控制更细粒度。如果停用词列表非常大则存储在其中 HashSet 进行快速搜索contains() 方法会比 replaceAll 循环更有效但这需要改变处理逻辑即将主文本分成单词然后逐个检查是否为停止单词。文本标准化在词频统计之前将所有词汇转换为小写是标准的。处理标点符号本教程使用 split([^a-zA-Z0-9]) 在大多数情况下移除标点符号就足够了。对于更复杂的场景可能需要更精细的标点符号处理逻辑。错误处理 为了应对文件不存在、权限不足等问题始终包括健壮的异常处理尤其是文件I/O操作。总结通过这个教程我们学习了如何使用Java NIO.2、Streamm字符串处理 API可以有效地删除文本文件中的停止词和词频统计。这个过程是许多文本分析任务的基础掌握这些技术将有助于更好地处理和理解文本数据。在实际应用中这些功能可以根据具体需要进一步优化和扩展如支持不同语言的停止词、更复杂的文本清洁规则等。

相关新闻