文本和日常工具·文本工具包
不可见字符:不间断空格、零宽度连接符和修剪
· 背景
文本清理 统一码 出版
调查看起来没什么的字符——不间断空格、零宽度空格和连接符、字节顺序标记——它们来自哪里,哪些算作空白,以及如何查找和删除它们。
两行不重复的相同行 — 隐形字符如何击败重复数据删除和搜索
两行在编辑器中看起来可能相同,但无法通过相等性、搜索或重复数据删除检查。一个可能包含普通的 U+0020 空格,而另一个包含 U+00A0(不间断空格)。零宽度字符可以在不占用任何可见宽度的情况下产生相同的问题,从而为发布者留下两个明显匹配的标签,这些标签仍然是不同的字符串。
这不仅仅是装饰性的。隐藏的代码点可以分割分析维度、破坏精确查找操作、保留重复行或使复制的标识符无法通过验证。在亲眼重写内容之前,保留源代码的副本并系统地比较可疑字符串。可见渲染是有关外观的证据,而不是其底层 Unicode 序列匹配的证据。
它们来自哪里 — 网页、文字处理程序、表情符号序列和 PDF 中的复制粘贴
隐形角色通常是通过普通工作来实现的。网页使用不间断空格将术语保持在一起,文字处理器保留面向布局的间距,并且 PDF 提取从定位字形重建文本。即使目标仅显示熟悉的单词和间隙,这些系统之间的复制也可以将格式化字符带入 CMS 字段。
其他隐形标记是书写系统或表情符号的有意部分。零宽度连接符可以将表情符号组件组合成一个显示的符号,而连接符和非连接符会影响多个脚本中的形状。来源很重要:“看不到它”并不意味着“可以安全删除”。清理应该针对已诊断的工件,而不是每个前进宽度恰好为零的字符。
空白家族 — 普通、不间断、狭窄和表意空间,以及 JavaScript 的修剪所考虑的空白
Unicode 包含的内容不仅仅是日常空间。 U+00A0 是不间断空格,U+202F 是窄不间断空格,U+3000 是表意空格。 JavaScript 空白处理包括这些字符,因此 `trim()`、`trimStart()` 和 `trimEnd()` 可以在它们出现在字符串的相关边缘时将其删除。
文本工具包的行修剪在每行上调用这些 JavaScript 方法。它不会标准化内部间距,因此两个单词之间仍保留不间断的空格。其“不带空格的字符”统计信息会删除与 JavaScript `s` 匹配的字符,该字符比 ASCII 空格更宽。使用该数字作为定义的测量,而不是作为排除每个不可见代码点的承诺。
零宽度系列 — 零宽度空格、连接符和非连接符、字连接符和字节顺序标记,它们根本不是空白
零宽度系列遵循不同的规则。 U+200B 零宽度空格、U+200C 零宽度非连接符、U+200D 零宽度连接符和 U+2060 单词连接符是格式字符而不是 JavaScript 空白。因此普通 `trim()` 不会删除它们。包含这些标记之一的行不会仅仅因为屏幕没有显示墨水而成为空白。
U+FEFF 有两个相关的历史:在编码数据的开头,它可以表示字节顺序标记,而在文本中,它充当零宽度不间断空格。 ECMAScript 在其修剪空白集中包含 U+FEFF,这与 U+200B 到 U+200D 不同。因此,将整个零宽度系列视为一种空白将与实际的 JavaScript 行为相矛盾。
检测它们 - 使用字符计数器来发现与您所看到的不匹配的计数,记住一些加入者会附加到邻居并保持隐藏状态
令人惊讶的计数可以提醒您隐藏的内容,但它无法识别每种情况。 ToolAcre 使用 `Intl.Segmenter` 计算字素簇(如果可用)。参与表情符号序列的连接器可能会帮助多个代码点形成一个字素,因此添加或删除它可以更改渲染,而不会产生代码点计数器将显示的简单的单字符增加。
同时使用多个线索:失败的精确匹配、意外的“无空格”结果、在支持 Unicode 的编辑器中检查复制的文本,或对特定代码点进行正则表达式搜索。当 Segmenter 不可用时,回退计数器使用代码点,因此结果也可能因浏览器功能而异。计数缩小了调查范围;它不是隐藏字符扫描器或 Unicode 名称查看器。
检测隐藏字符:计数提供线索,而不是完整的清单
对于已确认的复制粘贴工件,请处理副本并在正则表达式模式下打开“查找和替换”。搜索 `[]` 并替换为任何内容会删除整个文本中的零宽度空格和嵌入的 U+FEFF 字符。该工具使用 JavaScript 的全局标志编译模式,报告替换计数,并在模式无效时保持文本不变。
不会自动将该模式扩大到 `[-]`。该范围还删除了 U+200C 和 U+200D,它们可以更改脚本形状并将连接的表情符号拆分为单独的符号。仅当检查证明这些代码点是不需要的时才添加这些代码点。替换后,运行重复数据删除并将输出与发布前保留的原始内容进行比较。
工作示例:在去重复之前仅删除已确认不需要的零宽度字符
此清理不会解决双向控制字符、同形文字或与混淆文本相关的每个安全问题。方向标记可以改变视觉顺序,而同形文字攻击则使用彼此相似的不同可见字符。这两个问题都不能通过删除空格来解决,并且不加区分的不可见字符正则表达式可能会损坏合法的多语言内容,同时忽略实际风险。
该工具包也不会公开正则表达式标志(例如 Unicode 或多行模式)、突出显示每个即将到来的匹配项或单独逐步执行替换。替换整个文本中的所有操作,因此报告的计数和撤消操作是重要的保障措施。对于源代码、合法副本或不熟悉的脚本,请在进行批量编辑之前使用专用的 Unicode 工具检查每个字符。
要点——隐形并不意味着无害;文本工具包的计数器和正则表达式查找和替换无需离开浏览器即可公开并删除它们
隐形并不意味着空洞、可互换或有害。不间断空格是具有布局语义的空白;零宽度连接符可以携带整形语义; U+FEFF 在 JavaScript 修剪下的行为再次不同。准确的清理首先是命名代码点,了解它可能存在的原因并确定目标是否仍然需要其功能。
使用文本工具包作为受控的浏览器端工作台:计数可以暴露差异,正则表达式查找和替换可以删除精确定义的集合,而重复数据删除可以确认隐藏的差异已消失。保留原始文件,避免默认删除连接器,并验证渲染结果。狭隘的、可审查的更正比将所有看不见的 Unicode 视为碎片更安全。