
本文详解为何原始正则替换逻辑导致重复保留数字、无法真正“去重”,并提供基于捕获组的简洁、可靠解决方案,实现将形如 Il Piccolo # 22 settembre 2023 的多行文本压缩为唯一月份年份组合。
本文详解为何原始正则替换逻辑导致重复保留数字、无法真正“去重”,并提供基于捕获组的简洁、可靠解决方案,实现将形如 `il piccolo # 22 settembre 2023` 的多行文本压缩为唯一月份年份组合。
原始代码的问题核心在于:误将 match() 和 replace() 混用于提取与转换,且未做去重处理。text.match(/^.../gm) 仅提取完整匹配行(如 "Il Piccolo # 22 settembre 2023"),随后对每行执行两次 replace() —— 第一次保留 # 前缀和 月 年('$1 # $2'),第二次试图删去开头的 数字+月(^\d+\s\w+\s),但该模式在已修改的字符串中已不存在(因 $1 # $2 结果是 "Il Piccolo # settembre 2023",开头无数字),故第二次 replace() 实际无效。更关键的是:所有原始行都被独立处理并输出,未合并相同 月 年 的条目,导致重复(如两行 settembre 2023 均保留)。
正确做法应分三步:
- 精准提取关键字段(报纸名、月份、年份);
- 按 月+年 归组去重;
- 格式化输出唯一结果。
以下是优化后的实现:
function groupNewspapers(text) {
// 正则精确捕获:报纸名、月份、年份(忽略日数)
const regex = /^([a-zA-Z\s]+)\s#\s\d+\s(\w+)\s(\d{4})$/gm;
const matches = [...text.matchAll(regex)];
// 使用 Set 自动去重,键为 "月 年"
const uniqueGroups = new Map();
for (const match of matches) {
const [, newspaper, month, year] = match;
const key = `${month} ${year}`;
// 仅保存首次出现的报纸名(或可统一用固定名)
if (!uniqueGroups.has(key)) {
uniqueGroups.set(key, newspaper.trim());
}
}
// 格式化为 "报纸名 # 月 年",并按年份倒序排列(可选)
return Array.from(uniqueGroups, ([key, name]) =>
`${name} # ${key}`
).join('\n');
}
// 测试
const input = `Il Piccolo # 22 settembre 2023
Il Piccolo # 21 settembre 2023
Il Piccolo # 22 settembre 2022
Il Piccolo # 21 settembre 2022`;
console.log(groupNewspapers(input));
// 输出:
// Il Piccolo # settembre 2023
// Il Piccolo # settembre 2022
✅ 关键改进点:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 使用 matchAll() 获取全部捕获组,避免 match() 丢失子匹配;
- 用 Map 或 Set 确保 月 年 组合全局唯一;
- 正则 (\w+)\s(\d{4}) 显式分离月份与年份,语义清晰、不易出错;
- 移除冗余的 test() 预检(matchAll() 返回空迭代器时 Array.from() 自然为空数组,无需提前判断)。
⚠️ 注意事项:
- 若输入含大小写混合的月份(如 Settembre / settembre),建议在生成 key 前统一转小写:key =${month.toLowerCase()} ${year}``;
- 如需严格按原始顺序输出(而非 Map 插入顺序),可先提取所有 月 年,再用 Array.from(new Set()) 去重并保持首次出现顺序;
- 对于更复杂的报纸名(含连字符、撇号等),将正则中的 [a-zA-Z\s]+ 替换为 [^#]+ 更鲁棒(即匹配 # 之前所有非井号字符)。
此方案彻底规避了捕获组在链式 replace() 中的误用风险,以声明式数据处理替代过程式字符串操作,兼具可读性、健壮性与可维护性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










