Java统计文本成分需手动分类:英文及ASCII字母用isLetter(),中文用CJK_UNIFIED_IDEOGRAPHS判断,数字需区分半角与全角,标点需排除空格等空白字符,大文件须流式处理并复用正则Pattern。

统计中文字符、英文字符、数字和标点的出现次数
Java 中没有现成的「一键统计文本成分」方法,得靠 Character 类逐个判断。关键不是遍历快慢,而是分类逻辑是否覆盖边界情况。
-
Character.isLetter(c)能识别中英文字符(包括带音调的拉丁字母),但不识别中文标点(如“,”“。”,它们属于PUNCTUATION) -
Character.isDigit(c)只匹配 ASCII 数字'0'–'9',不匹配全角数字(如“1”),需额外判断 Unicode 范围\uFF10–\uFF19 - 中文字符不能靠
isLetter()判断——汉字返回false,正确方式是检查 Unicode Block:Character.UnicodeBlock.of(c) == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS - 空格、制表符、换行符属于
Character.isWhitespace(c),别误归为标点
String text = "Hello世界123!"; Mapcount = new HashMap<>(); for (char c : text.toCharArray()) { if (Character.isLetter(c)) { count.merge("letter", 1, Integer::sum); } else if (Character.isDigit(c)) { count.merge("digit", 1, Integer::sum); } else if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS) { count.merge("chinese", 1, Integer::sum); } else if (Character.isWhitespace(c)) { count.merge("whitespace", 1, Integer::sum); } else { count.merge("punctuation", 1, Integer::sum); } }
按词频统计(含中文分词基础处理)
纯用 String.split() 对中文无效——它按空格/标点切,但“我喜欢编程”会变成一个长词。真要词频,至少得上简易规则:按中文字符、英文单词、数字三类分别提取。
- 正则
[\\u4e00-\\u9fa5]+匹配连续中文字符(覆盖常用汉字) -
[a-zA-Z]+提取英文单词,注意加Pattern.CASE_INSENSITIVE更稳妥 -
\\d+提取连续数字,避免单个数字被拆散 - 忽略大小写统一转小写,但中文无需此步
- 停用词(如“的”“了”)需手动过滤,Java 标准库不提供内置停用词表
String text = "Java很强大,Java也很好学!"; Listwords = new ArrayList<>(); // 匹配中文词 Matcher cnMatcher = Pattern.compile("[\\u4e00-\\u9fa5]+").matcher(text); while (cnMatcher.find()) words.add(cnMatcher.group()); // 匹配英文单词 Matcher enMatcher = Pattern.compile("[a-zA-Z]+").matcher(text); while (enMatcher.find()) words.add(enMatcher.group().toLowerCase()); // 统计 Map freq = words.stream() .filter(w -> w.length() > 1) // 过滤单字(可选) .collect(Collectors.groupingBy(w -> w, Collectors.counting()));
处理大文件时避免内存溢出
用 Files.readAllLines() 读 GB 级文本直接 OOM。必须流式处理,且避免在循环里反复创建 StringBuilder 或正则对象。
- 用
Files.lines(Paths.get("file.txt"))返回Stream,配合forEach或reduce - 正则
Pattern实例应提前编译并复用,不要在循环里写Pattern.compile(...) - 统计 Map 推荐用
ConcurrentHashMap(多线程安全),或单线程下用HashMap+merge()避免 get-put 冗余操作 - 若需按行号定位,改用
BufferedReader,它比lines()更易控制异常和关闭资源
Pattern wordPattern = Pattern.compile("[\\u4e00-\\u9fa5]+|[a-zA-Z]+|\\d+");
try (Stream lines = Files.lines(Paths.get("huge.txt"))) {
lines.flatMap(line -> wordPattern.matcher(line).results()
.map(MatchResult::group)
.map(String::toLowerCase))
.filter(word -> !word.isEmpty())
.collect(Collectors.toConcurrentMap(
word -> word,
word -> 1L,
Long::sum
));
}
区分全角/半角字符与编码陷阱
同一个“.”,ASCII 点(.,U+002E)和中文句号(。,U+3002)Unicode 编码不同,String.equals() 判定为不等。项目若涉及用户输入或混合来源文本,这点极易漏测。
立即学习“Java免费学习笔记(深入)”;
- 读文件时务必指定编码:
Files.readAllLines(path, StandardCharsets.UTF_8),否则 Windows 默认 GBK 会把 UTF-8 文件读成乱码,导致字符识别全错 - 全角数字、字母、标点有独立 Unicode 区段(如全角 A 是
\uFF21),Character.isLetter()对它们返回false,需单独处理 - 测试用例必须包含混合编码字符串,例如:
"abc123ABC。!",验证是否把全角字符归入对应类别 - 日文平假名、片假名属于
Character.UnicodeBlock.HIRAGANA/KATAKANA,若需求含日语,也要显式判断










