当前位置: 首页 > news >正文

从《哈利·波特》到代码:用Java词频统计,轻松分析你最爱的小说角色

从《哈利·波特》到代码:用Java词频统计揭秘小说角色热度

当你第三次重读《哈利·波特》系列时,是否好奇过罗琳笔下哪位角色出场次数最多?传统的手工统计显然不现实,但借助Java编程,我们完全可以像魔法一样自动解开这个谜题。本文将带你用HashMap字符串处理技术,构建一个能够分析任意小说文本的词频统计系统,最终输出角色人气排行榜。

1. 项目准备与环境搭建

在开始编码冒险之前,我们需要准备好"魔法道具"。这个项目只需要最基本的Java开发环境:

  • JDK 8+:任何现代版本均可
  • 文本编辑器:IntelliJ IDEA、Eclipse或VS Code
  • 小说文本文件:建议从Project Gutenberg等合法渠道获取

创建新Java项目后,在项目根目录下新建resources文件夹,放入准备好的《哈利·波特》小说文本文件(如harry_potter.txt)。文本文件应当满足以下格式要求:

Harry looked at Ron and Hermione. "Let's go," Harry said. The Dursleys had never mentioned anything about Hogwarts...

提示:处理中文小说时需注意编码问题,建议统一使用UTF-8格式

2. 文本预处理与单词分割

原始文本包含各种标点和格式噪音,我们需要先进行数据清洗:

public class TextPreprocessor { public static String cleanText(String rawText) { // 转换为小写统一处理 String cleaned = rawText.toLowerCase(); // 移除所有标点符号(保留单词间空格) cleaned = cleaned.replaceAll("[^a-zA-Z\\s]", ""); // 合并连续空格 cleaned = cleaned.replaceAll("\\s+", " "); return cleaned.trim(); } }

接下来实现两种经典的分词方案对比:

分词方法优点缺点适用场景
String.split()原生支持、简单直接性能较差简单分隔
StringTokenizer高效、灵活API较旧复杂文本处理
// 使用StringTokenizer的示例 public List<String> tokenizeWithStringTokenizer(String text) { List<String> tokens = new ArrayList<>(); StringTokenizer tokenizer = new StringTokenizer(text); while (tokenizer.hasMoreTokens()) { tokens.add(tokenizer.nextToken()); } return tokens; }

3. 核心词频统计实现

我们采用HashMap作为核心数据结构,其O(1)的查询复杂度非常适合词频统计:

public Map<String, Integer> countWordFrequencies(List<String> words) { Map<String, Integer> frequencyMap = new HashMap<>(); for (String word : words) { // 如果单词长度过短可能是无意义词 if (word.length() < 3) continue; frequencyMap.put(word, frequencyMap.getOrDefault(word, 0) + 1); } return frequencyMap; }

针对小说分析的特殊需求,我们可以添加角色名字典过滤:

public static final Set<String> HP_CHARACTERS = Set.of( "harry", "ron", "hermione", "dumbledore", "voldemort", "snape", "hagrid" ); public Map<String, Integer> filterCharacterFrequencies( Map<String, Integer> fullMap) { return fullMap.entrySet().stream() .filter(entry -> HP_CHARACTERS.contains(entry.getKey())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); }

4. 结果排序与可视化输出

统计完成后,我们需要按词频降序排列结果:

public List<Map.Entry<String, Integer>> sortByFrequency( Map<String, Integer> frequencyMap) { List<Map.Entry<String, Integer>> entries = new ArrayList<>(frequencyMap.entrySet()); entries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue())); return entries; }

添加可视化输出增强可读性:

public void printCharacterRanking(List<Map.Entry<String, Integer>> ranked) { System.out.println("《哈利·波特》角色出场次数排行榜"); System.out.println("============================"); for (int i = 0; i < ranked.size(); i++) { Map.Entry<String, Integer> entry = ranked.get(i); System.out.printf("%d. %-10s: %5d次 | %s%n", i + 1, capitalize(entry.getKey()), entry.getValue(), generateBar(entry.getValue())); } } private String generateBar(int count) { return IntStream.range(0, Math.min(count / 100, 50)) .mapToObj(i -> "█") .collect(Collectors.joining()); }

5. 完整流程整合与优化

将所有模块串联成完整流程:

public class NovelAnalyzer { public void analyze(String filePath) throws IOException { // 1. 读取文本文件 String rawText = Files.readString(Paths.get(filePath)); // 2. 文本清洗 String cleanedText = TextPreprocessor.cleanText(rawText); // 3. 单词分割 List<String> words = tokenizeWithStringTokenizer(cleanedText); // 4. 词频统计 Map<String, Integer> frequencies = countWordFrequencies(words); // 5. 角色过滤 Map<String, Integer> characterFreq = filterCharacterFrequencies(frequencies); // 6. 结果排序 List<Map.Entry<String, Integer>> ranked = sortByFrequency(characterFreq); // 7. 可视化输出 printCharacterRanking(ranked); } }

执行效果示例:

《哈利·波特》角色出场次数排行榜 ============================ 1. Harry : 4235次 | █████████████████████████████████████████████ 2. Ron : 1987次 | ████████████████████████ 3. Hermione : 1564次 | ███████████████████ 4. Dumbledore: 987次 | ███████████ 5. Snape : 845次 | █████████ 6. Voldemort : 672次 | ███████ 7. Hagrid : 543次 | ██████

6. 进阶优化与扩展思路

基础版本完成后,可以考虑以下增强功能:

  • 词干提取:处理不同词形变化(如"running"→"run")
  • 停用词过滤:忽略"the"、"and"等无意义词
  • 上下文分析:统计角色同时出现的场景
  • 情感分析:结合情感词典分析角色相关文本的情绪倾向

添加NLP扩展的示例:

// 使用OpenNLP进行命名实体识别 public Set<String> detectCharacters(String text) throws IOException { InputStream modelIn = getClass().getResourceAsStream("/en-ner-person.bin"); TokenNameFinderModel model = new TokenNameFinderModel(modelIn); NameFinderME nameFinder = new NameFinderME(model); String[] sentences = text.split("[.!?]"); Set<String> characters = new HashSet<>(); for (String sentence : sentences) { String[] tokens = sentence.split("\\s+"); Span[] nameSpans = nameFinder.find(tokens); for (Span span : nameSpans) { characters.add(String.join(" ", Arrays.copyOfRange(tokens, span.getStart(), span.getEnd()))); } } return characters; }

这个项目最有趣的部分在于,你可以自由替换文本文件来分析任何文学作品。试过《魔戒》中的角色热度吗?或者比较不同翻译版本的《三国演义》?代码不变,只需更换文本文件就能开启新的探索。

http://www.jsqmd.com/news/953505/

相关文章:

  • 2025-2026年韩国留学机构推荐:五大口碑评测普通家庭留学避坑攻略专业价格 - 品牌推荐
  • 酒泉市2026年最新黄金回收白银回收铂金回收门店实测 五家靠谱店铺排行榜及联系方式电话推荐 - 盛世金银回收
  • 基于Arduino的语音交互系统:从硬件搭建到代码实现全解析
  • LabVIEW 2018 新手必看:用随机数模拟温度,5分钟搞定一个报警系统(附源码)
  • 实战演练:基于快马平台快速构建你的第一个简易汇编器与指令模拟器
  • 【包头+本地黄金回收+闲置金饰现场变现攻略】 - 余生黄金回收
  • 当栈溢出遇上No RELRO:一个ret2dlresolve利用的‘捷径’与64位下的‘坑’
  • 【扬州黄金回收门店报价盘点】 - 余生黄金回收
  • 开封市2026年最新黄金回收白银回收铂金回收门店实测 五家靠谱店铺排行榜及联系方式电话推荐 - 盛世金银回收
  • Invoke-AtomicRedTeam实战:使用原子测试验证EDR防护效果的完整教程
  • 如何突破AI编程工具的限制:go-cursor-help让Cursor重获新生的故事
  • AI备课、学情诊断、动态分层——3类高复用智能教学工作流,即装即用(附教育部认证工具白名单)
  • 终极英雄联盟工具箱:基于LCU API的完整自动化解决方案
  • ai开发新范式,让快马平台的ai助手帮你优化yolov11模型性能
  • 【包头+正规黄金回收+全区域上门估价变现】 - 余生黄金回收
  • 基于555定时器的LED呼吸灯电路设计与骷髅眼制作教程
  • 昆明市2026年最新黄金回收白银回收铂金回收门店实测 五家靠谱店铺排行榜及联系方式电话推荐 - 盛世金银回收
  • 揭秘gh_mirrors/spi/spider核心功能:5大特性让你的爬虫效率提升300%
  • 哪家环境安全检测产品公司专业?2026年6月推荐TOP5对比案例评测注意事项市场份额 - 品牌推荐
  • 如何用Kronos金融预测模型实现精准市场分析:从入门到精通的完整指南
  • 别再用IMDB练手了!试试这个46分类的新闻数据集,用Keras实战文本分类(附完整代码)
  • 别再死记ResNet了!用PyTorch从零复现DenseNet-121,彻底搞懂‘密集连接’
  • 电线焊接可靠性指南:从交叉焊到绞合焊的强度对比与实操技巧
  • 数据科学家成长瓶颈突破:隐性知识与结构化mentorship实战指南
  • 如何微调POINTS-Seeker:自定义多模态代理搜索模型训练指南
  • MATLAB双目视觉实战包:ORB特征匹配、实时跟踪与深度距离计算全链路代码
  • 【包头+六大黄金回收门店+旧金/投资金条上门变现】 - 余生黄金回收
  • 如何快速掌握COLMAP三维重建:从零基础到专业应用的完整指南
  • Arduino Leonardo实现自定义HID设备:物理按钮切换浏览器标签页
  • 量子测量误差缓解技术:从原理到实践