首页 > 解决方案 > 错误的unicode字符串,看起来一样但本质不一样

问题描述

错误文本 unicode

当我使用 CURL 来获取一个网站的内容时,我得到的内容看起来是一样的,但实际上是不同的。这会影响文档的处理和比较。有没有办法将 $loi 转换为 $check 标准格式,以便我可以正确处理它?

您可以将内容 $loi 或 $check 复制到 cmd 窗口中,以立即看到如图所示的差异

$loi = 'người được tiêm';
    $check = 'người được tiêm';
    var_dump($loi);
    var_dump($check);

标签: stringcurlunicodeutf-8

解决方案


有些代码点在 Unicode 中看起来相同,因为它们具有相似甚至相同的字素,但实际上并不相同。

当您意识到 Unicode 的目的是容纳尽可能多的语言时,这应该不足为奇,而且它通常表示字母的用途,而不是形式。

例如U+2010,and U+2011(连字符和不间断连字符)可能看起来完全相同,因为后者只是前者的不间断版本。

如果您将两个字符串泵入Unicode 到代码点转换器,您会看到不同之处。

为简洁起见,我只完成了每个单词的第一个单词,并给出了十六进制的代码点,每个“字符”周围都有方括号:

người [6e] [67] [75 31b] [6f 31b 300] [69]
người [6e] [67] [1b0]    [1edd]       [69]

例如,第一个中的 是75 31bLatin small letter U后跟combining horn(字母的修饰符)。在第二个中,它是单个1b0, Latin small letter U with horn(已经内置在代码点中的修饰符)。

同样,ờ6f 31b 300第一个中,三个单独的代码点分别表示Latin small letter Ocombining horn修饰符和combining grave accent修饰符。第二个具有这1edd两个修饰符已经合并到单个代码点中,Latin small letter O with horn and grave.

因此,在这些情况下,它实际上并不是字素的不同意图,而是一种不同的表示方式,或者:

  • 带有内置修饰符的单个代码点;或者
  • 具有单独的附加修饰符代码点的代码点。

如果您需要对它们一视同仁 Unicode 有一个等价规范化的概念。

等价表示多个代码点序列实际上是同一“事物”的变体,而规范化是将等价物映射到单个变体的过程,以便比较容易。

在 Python 中,我将使用以下方法来映射一种或另一种方式:

import unicodedata
normalised_composed = unicodedata.normalize('NFC', 'người'))
normalised_decomposed = unicodedata.normalize('NFD', 'người'))
# Composed is short sequence (minimal codepoints), decomposed is long.

以下成绩单显示了输出,尽管我已经重新格式化和注释以提高可读性:

>>> bytearray('người', 'utf-16')
bytearray(b'\xff\xfe                # Unicode BOM for UTF-16.
    n\x00                           # n.
    g\x00                           # g.
    u\x00 \x1b\x03                  # u, combining horn.
    o\x00 \x1b\x03 \x00\x03         # o, combining horn & grave.
    i\x00                           # i.
')

>>> bytearray(unicodedata.normalize('NFD', 'người'), 'utf-16')
bytearray(b'\xff\xfe                # Identical to previous, it
    n\x00                           #   was already decomposed.
    g\x00
    u\x00 \x1b\x03
    o\x00 \x1b\x03 \x00\x03
    i\x00
')

>>> bytearray(unicodedata.normalize('NFC', 'người'), 'utf-16')
bytearray(b'\xff\xfe                # BOM.
    n\x00                           # n.
    g\x00                           # g.
    \xb0\x01                        # Latin u with horn.
    \xdd\x1e                        # Latin o with horn & grave.
    i\x00                           # i.
')

我不完全确定您使用的是什么语言(目前没有标签)但是,如果它声称可以处理 Unicode,它应该有等效的功能来执行此操作(因此,如果您稍后添加标签,我仍然认为这个答案很有用)。

只需<your_language> unicode normalisation在您选择的搜索引擎中搜索即可。


推荐阅读