我正在用 Javascript 比较两个可能不同编码的文件名,希望找到匹配项:
- 一个文件名是实际文件名,来自未存档的 zip(使用 https://stuk.github.io/jszip/ )
- 一个文件名是从 bplist 中提取的名称(iOS 存档格式使用 https://github.com/joeferner/node-bplist-parser 取消存档)
分析
当比较 JavaScript 控制台中的日志输出时,这些文件名看起来完全相同:
15 - Beschänkt und gsägnet - PLAYBACKVERSION.mp3
15 - Beschänkt und gsägnet - PLAYBACKVERSION.mp3
注意德语变音符号。
现在,当我将这些字符串复制并粘贴到 Notepad++ 并启用十六进制编辑器时,它看起来像这样:
- 在第一种情况下,A-Umlaut 使用 3(三) 字节进行编码
- 在第二种情况下,A-Umlaut 仅使用 2(两个)字节进行编码。
问题
如何安全地比较这两个字符串。 Javascript 中是否有通用的“unencode”方法可以处理这些实例?或者我应该/必须猜测每种编码,然后显式比较吗?
注意
- 我特别要求 JavaScript 解决方案
- 这个问题,Compare strings with different encodings尽管类似实际上与编码无关
最佳答案
这里发生了什么?
如果 JavaScript 中有一个 String
,它就是一个 Unicode 代码点序列。某些组件已经将 ZIP 或 plist 中表示这些字符串的字节解码为代码点序列。
也就是说,这个问题并不完全是关于编码,而是关于 Unicode 分解和规范化形式。
可以在 Unicode 中以(至少)两种不同的方式对 ä 进行编码(由于有用的输出,下面的示例在 Python 中)。
>>> "ä".encode("UTF-8")
b'\xc3\xa4' # two bytes
>>> [ord(c) for c in "ä"]
[228]
>>> [unicodedata.name(c) for c in "ä"]
['LATIN SMALL LETTER A WITH DIAERESIS']
或者采用 NFKD 规范化形式,采用 UTF-8 格式的两个代码点和三个字节。
>>> unicodedata.normalize("NFKD", "ä").encode("UTF-8")
b'a\xcc\x88' # three bytes
>>> [ord(c) for c in unicodedata.normalize("NFKD", "ä")]
[97, 776] # two codepoints
>>> [unicodedata.name(c) for c in unicodedata.normalize("NFKD", "ä")]
['LATIN SMALL LETTER A', 'COMBINING DIAERESIS']
回答
长话短说,在 JavaScript 中,您需要调用 String#normalize()
在尝试常规比较之前确保字符串处于相同的规范化形式。
$ node
Welcome to Node.js v16.6.1.
Type ".help" for more information.
> var a = '15 - Beschänkt und gsägnet - PLAYBACKVERSION.mp3';
undefined
> var b = '15 - Beschänkt und gsägnet - PLAYBACKVERSION.mp3';
undefined
> a.length
50
> b.length
48
> a === b
false
> a.normalize() === b.normalize()
true
>
关于Javascript:比较两个具有实际不同编码的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69177720/