- 教程
- 文档
【免费下载链接】30-seconds-of-code
Coding articles to level up your development skills
本文基于 30-seconds-of-code 仓库中的 remove-accents.md 文档,系统讲解如何在 JavaScript 中快速去除字符串的重音符号(accent/diacritical mark)。你将掌握String.prototype.normalize('NFD')与正则表达式的组合用法,并理解其背后的 Unicode 规范化原理,从而在字符串比较、搜索、存储与 slug 生成等场景中直接落地应用。
为什么需要去除重音符号
在 JavaScript 开发中,处理非英文字符串时常常会遇到带重音符号的文本,例如法语中的é、è、ê,德语中的ä、ö、ü,以及西班牙语中的ñ等。这些字符在视觉上直观易懂,但在程序处理中却可能带来一系列麻烦:
- 字符串比较失真:
'café' === 'cafe'的结果为false,即使语义上用户可能认为它们相同。去除重音后即可让这类比较"宽容"地通过; - 搜索与索引不便:用户输入
cafe却搜不到数据库里的café,去重音可以让搜索关键字与存储内容对齐; - 存储与展示规范化:部分系统、旧数据库或外部接口对非 ASCII 字符支持不佳,将字符串归一化为纯 ASCII 形式更利于存储、传输和跨系统兼容。
正如原文档所述,去除重音的核心诉求就是:在比较字符串时不考虑重音,以及在存储或展示前将字符串规范化。
核心方案:normalize('NFD') + 正则替换
表面上看这是一件困难的事,但实际上 JavaScript 内置的String.prototype.normalize()方法让它可以"一行搞定"。原文档给出的实现如下:
const removeAccents = str => str.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); removeAccents('Antoine de Saint-Exupéry'); // 'Antoine de Saint-Exupery'整个方案只分两步:
- 使用
normalize('NFD')将字符串转换为NFD(Normalization Form Canonical Decomposition)规范化形式,把组合字符拆分为"基础字符 + 变音标记"两部分; - 使用
replace()配合正则/[\u0300-\u036f]/g匹配 Unicode 中的组合变音标记(Combining Diacritical Marks)区间,并将其替换为空字符串,从而把变音标记从字符串中彻底移除。
NFD 规范化做了什么
Unicode 中存在两种字符表示方式:预组合字符(precomposed)与组合序列(decomposed)。例如:
é(U+00E9)是预组合字符,直接用一个码点表示;e+◌́(U+0065 + U+0301)是组合序列,用"基础字母 + 组合重音符"两个码点表示。
normalize('NFD')的作用就是把预组合字符拆解成等价的组合序列。以'é'为例,NFD 之后它变成'e'加上一个组合音调符号。这样一来,基础字符(字母)与变音标记就被分离到了不同的码点上,后续用正则按码点区间匹配变音标记并删除,就变得非常简单。
需要注意的是,String.prototype.normalize()支持四种规范化形式:'NFC'、'NFD'、'NFKC'、'NFKD',其中NFD和NFKD是分解形式(decomposition),NFC与NFKC是组合形式(composition)。去重音必须使用分解形式NFD,因为只有分解才能把变音标记"拆"出来供正则删除。该方法的详细行为可以参考仓库 content/languages/javascript.yaml 中收录的String.prototype.normalize()文档链接。
理解 \u0300-\u036f 区间
正则中的[\u0300-\u036f]对应 Unicode 的Combining Diacritical Marks块(U+0300 至 U+036F),该区间包含组合重音符(combining acute accent)、组合抑音符(combining grave accent)、组合扬抑符、组合分音符等绝大多数拉丁字母系统常用的变音标记码点。加上g标志(global)后,replace()会扫描整个字符串,删除所有命中的变音标记。
上面的示例中,'Antoine de Saint-Exupéry'经过normalize('NFD')后,é被拆解为e+ 组合重音符,随后组合重音符被正则匹配并删除,最终得到纯 ASCII 形式的'Antoine de Saint-Exupery'。
进阶组合:去重音 + 转小写 + 生成 slug
去除重音很少单独使用,通常它会与字符串的小写化、修剪、特殊字符清理等操作组合,构成一个完整的规范化流水线。例如,在仓库的 string-to-slug.md 中,把字符串转换为 SEO 友好的 slug 时,常规流程是:转小写 → 去除特殊字符 → 将空白与分隔符替换为连字符。若在转小写之前先执行removeAccents,就能让'Café Décor'这类带重音文本顺利产出'cafe-decor'形式的干净 slug:
const removeAccents = str => str.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); const slugify = str => removeAccents(str) .toLowerCase() .trim() .replace(/[^\w\s-]/g, '') .replace(/[\s_-]+/g, '-') .replace(/^-+|-+$/g, ''); slugify('Café Décor!'); // 'cafe-decor'同理,仓库中的 is-anagram.md 展示了另一种规范化思路:通过toLowerCase()与replace()正则去除非必要字符后再排序比较。去重音逻辑完全可以作为这类比较场景的前置步骤,保证'Aé'与'ae'这类输入在比较时被视为等价。
结合场景一:大小写与重音不敏感的字符串比较
去除重音只是字符串规范化的一种手段。如果目标只是"比较字符串时忽略大小写和重音",而不想物理上修改原字符串,仓库中的 compare-string-case-accent-insensitive.md 提供了另一种更贴近语言环境的选择——Intl.Collator:
const arr = ['ä', 'a', 'b', 'A', 'B', 'Å']; const collator = new Intl.Collator(); const deCollator = new Intl.Collator('de'); const svCollator = new Intl.Collator('sv'); arr.sort(collator.compare); // ['a', 'A', 'Å', 'ä', 'b', 'B'] arr.sort(deCollator.compare); // ['a', 'A', 'Å', 'ä', 'b', 'B'] arr.sort(svCollator.compare); // ['a', 'A', 'b', 'B', 'Å', 'ä']两者的定位不同:Intl.Collator适合需要遵守特定语言排序规则(例如瑞典语中Å应排在字母表末尾)的场景;而removeAccents适合需要把字符串本身归一化为纯 ASCII的场景(例如生成 URL、做不区分重音的搜索键)。实际选型时应按需求二选一或组合使用:去重音得到规范化副本用于存储与匹配,Intl.Collator用于保持语言敏感的输出排序。
注意事项与边界情况
尽管normalize('NFD') + replace是最简洁高效的通用方案,使用时仍需留意以下几点:
- 并非所有字符都会被 NFD 分解:部分字符(如丹麦语/挪威语的
ø、德语的ß、带斜线的ł等)在 NFD 下不会拆出可被\u0300-\u036f匹配的组合标记,因此去重音对它们不生效。这类字符需要另行映射处理; - 组合标记区间之外:
\u0300-\u036f只覆盖 Combining Diacritical Marks 块,个别扩展变音标记位于其他 Unicode 块(如 U+1AB0 起的扩展块),如遇极特殊文本可考虑扩展区间; - 只去变音、不动字母:该方案只删除变音标记,基础字母本身(包括非 ASCII 字母)会被保留,这正是"去除重音"而非"全部转 ASCII"的语义所在;
- 适用于拉丁语系文本:对阿拉伯文、希伯来文、梵文等其它文字系统,本方案无法起到类似作用。
总结
30-seconds-of-code 仓库的 remove-accents.md 用两行代码就解决了 JavaScript 字符串去重音的经典问题,其技术内核是Unicode NFD 规范化 + 组合变音标记区间的正则替换。在实际项目中,把它与转小写、slug 化、Intl.Collator比较等手段灵活组合,即可构建出可靠的字符串规范化体系,覆盖比较、搜索、存储与 URL 生成等常见业务诉求。
- 教程
- 文档
【免费下载链接】30-seconds-of-code
Coding articles to level up your development skills
相关推荐
30 Seconds of Code:JavaScript 百分位计算(percentile)实现与原理详解
30 Seconds of Code:JavaScript 百分位计算(percentile)实现与原理详解 导读 在 30 Seconds of Code 项
教程文档30 seconds of code:Python 字符串去除空白字符完全指南(strip / lstrip / rstrip)
30 seconds of code:Python 字符串去除空白字符完全指南(strip / lstrip / rstrip) 在 Python 日常开发中,
教程文档30 Seconds of Code:用 JavaScript 正则表达式规范化字符串行结束符(Line Endings)
30 Seconds of Code:用 JavaScript 正则表达式规范化字符串行结束符(Line Endings) 导读 在跨平台开发的日常中,文本文件
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考