关于本文
本文是通过利用生成式 AI 的自动生成流程创建的。我们对照了 .NET 的 String.Normalize 和 NormalizationForm.FormKC 的官方规范,并编写了使用 PowerShell 观察 Unicode 规范化的示例。尚未在真实的 PowerShell 环境中进行运行验证。
验证状态:📘 已确认 .NET 官方规范/未在 PowerShell 实机中验证
如果在 Excel 或 CSV 中混杂了“ABC123”和“ABC123”,它们虽然看起来几乎一样,但在比较时可能会被视为不同的值。混入半角片假名或带圈数字时也是如此。
.NET 的String.Normalize中指定NormalizationForm.FormKC时,可以通过Unicode 兼容规范化(NFKC)来统一许多书写差异。但是,由于 NFKC 包含一些无法恢复原始书写形式的转换,因此不能无条件地将其应用于商品代码、员工 ID、密码等内容。
在 PowerShell 中尝试最小示例
假设环境支持使用 .NET 的(例如 PowerShell 7 或 Windows PowerShell 5.1等)。String.Normalize可以使用的环境。
$before = 'ABC123' $after = $before.Normalize([Text.NormalizationForm]::FormKC) "Before: $before" "After : $after"
预期的显示结果如下。这是根据官方规范预测的结果,并非实机输出。
Before: ABC123 After : ABC123
FormKC是执行兼容分解和规范合成的 NFKC 规范化。常见的FormC(NFC)主要处理规范等价性,其用途与兼容字符的统一不同。
比较各种字符
在下面的示例中,我们将转换前后的内容制成表格,以确认内容是否确实发生了变化。输入仅为虚拟字符串,不会写入文件或注册表。
$values = @(
'ABC123'
'カタカナ'
'① Ⅳ'
)
$results = foreach ($original in $values) {
$normalized = $original.Normalize([Text.NormalizationForm]::FormKC)
[pscustomobject]@{
Before = $original
After = $normalized
Changed = ($original -cne $normalized)
}
}
$results | Format-Table -AutoSize
预期的转换为:ABC123→ABC123、カタカナ→カタカナ、① Ⅳ→1 IV。从带圈数字和罗马数字的显示发生变化这一点也可以看出,这并非仅仅是删除空格或更改英文字母和数字的字宽。
相同的代码已保存于Daily-Code-Samples 的 NFKC 实验中。该示例仅进行读取和显示,不会擅自覆盖 CSV。
修改一处并与 NFC 进行比较
FormKC将其FormC更改为以比较相同的字符。
$text = 'ABC123' $text.Normalize([Text.NormalizationForm]::FormC) $text.Normalize([Text.NormalizationForm]::FormKC)
FormC时全角英数字保持不变,而FormKC时则预期统一为半角英数字。这并非哪一个是“绝对正确”,而是取决于你想把什么视为相同的字符来做出选择。
.NET 官方的String.Normalize和NormalizationForm中对规范化形式进行了说明。
如果在工作中应用,请保留“规范化前”的数据
例如,在销售数据的商品名称栏中,如果因输入人员不同而混杂了全角和半角字符,为了便于搜索和汇总,额外添加一列经过规范化的数据会非常方便。
$rows = @(
[pscustomobject]@{ Item = 'ABC123'; Count = 2 }
[pscustomobject]@{ Item = 'ABC123'; Count = 3 }
)
$rows | Select-Object Item, Count, @{
Name = 'SearchKey'
Expression = {
$_.Item.Normalize([Text.NormalizationForm]::FormKC)
}
}
这里保留了原始的 Item 并添加了新的SearchKey。由于不会丢失原始数据,因此事后可以确认“是否可以将其当作相同的字符串处理”。
注意: 不同的原始字符串有时会生成相同的 SearchKey。因此,如果要通过员工编号或商品代码来保证唯一性,请在规范化后检查是否会出现重复,然后再将其用作键。
切勿错误统一的示例
| 数据 | 应用 NFKC 的思路 |
|---|---|
| 用于辅助搜索的品名 | 可以与原始字符串在单独的列中尝试规范化 |
| 输入的半角/全角英数字说明文 | 根据用途,统一处理会很方便 |
| 商品代码、管理编号 | 需要验证规范化后是否会发生不同 ID 冲突 |
| 密码、签名对象 | 含义或比对结果会发生改变,因此请勿擅自修改 |
| 文件名 | 确认是否存在与现有路径的不一致或冲突 |
此外,Unicode 规范化并非将“所有看起来相似的字符”等同视之。它并不是万能的处理方式,无法解决不同文字系统中外观相似的字符、不可见字符以及尾部空白等所有问题。
失败、边界条件
输入为$null如果直接调用字符串方法将会失败。对于从 CSV 或 API 接收的数据,请首先检查它是否为 null。
function ConvertTo-Nfkc([AllowNull()][string]$Value) {
if ($null -eq $Value) { return $null }
return $Value.Normalize([Text.NormalizationForm]::FormKC)
}
对于由于 Unicode 规范化失败而导致的无效字符串,在业务场景中使用时也应考虑异常处理。此外,PowerShell 中的-eq等比较运算符默认不区分大小写,因此如果需要严格区分大小写进行判定,请使用-ceq或-cne。
