同一个单词但不同的Unicode字符

3

我使用Windows上的Python构建了一个有关越南餐厅的问答应用程序。为了编写越南字符,我需要使用Unicode。
首先,我从使用HTML charset=utf-8的TripAdvisor网站克隆数据并构建了我的Mongo数据库。 TripAdvisor中名为“đà nẵng”的城市有一个代码:

>>> print repr("đà nẵng")     # from tripadvisor website 
>>> '\xc4\x91a\xcc\x80 n\xc4\x83\xcc\x83ng'

然而,当我从Firefox的地址栏查询时,城市“đà nẵng”的代码不同:

>>> print repr("đà nẵng")   # Firefox's address bar
>>> '\xc4\x91\xc3\xa0 n\xe1\xba\xb5ng'

这就是我在数据库中找不到那个城市的原因。我试着在Notepad++上写下这个城市的名称,结果跟在Firefox地址栏里输入一样。
>>> print repr("đà nẵng")   # notepad++ using 'Encoding UTF-8'
>>> '\xc4\x91\xc3\xa0 n\xe1\xba\xb5ng'

有没有办法在两种编码之间进行转换?
或者在这种情况下,有没有办法将城市名称“đà nẵng”与不同的编码匹配?


尝试使用str("đà nẵng", 'utf-8') - Nether
@Nether 1) 那不是问题 2) 只有在您的终端是UTF-8或您的代码编码是UTF-8时,才会进行解码 - Alastair McCormack
2个回答

6
你遇到的问题是,Unicode 允许使用多种方式组合相同的符号。Python 模块 unicodedata 提供了一个 normalize 函数,允许你将 Unicode 表示转换为固定的格式(例如 NFC)。
from unicodedata import normalize

S1 = b'\xc4\x83\xcc\x83'.decode('UTF-8')
S2 = b'\xe1\xba\xb5'.decode('UTF-8')

print(normalize('NFC', S1).encode('UTF-8'))
print(normalize('NFC', S2).encode('UTF-8'))

在您的示例中,Tripadvisor 是以 NFD 形式显示的,而 Notepad 使用的是 NFC。


3

虽然这是一个老问题,但我会为那些偶然遇到此问题的人添加答案。

两个字节序列之间的差异是规范等价的一种情况。某些字符可以用多个字节序列表示。对于字母ẵ,有五种规范等效表示可能。在Python中,可以使用pyicu获取特定字符串的所有规范等效列表。

其中两个等价于使用NFC和NFD的标准化形式。但在这个问题的示例中,Firefox使用NFC来表示nẵng,字节序列为b'n\xe1\xba\xb5ng'。

但从TripAdvisor给出的字节序列是b'n\xc4\x83\xcc\x83ng`。这不是NFC,也不是NFD。NFD等效形式应该为b'na\xcc\x86\xcc\x83ng'。

TripAdvisor中的字符串是使用Windows越南语键盘输入的。当Microsoft首次实现越南语Unicode键盘时,他们遵循了Windows-1258的字符模型,带有抑扬符、短音符或钩子的元音是单个预组合字符,所有声调标记都由组合变音符号表示,因此当时产生的文本是预组合和分解序列的混合。既不是NFC也不是NFD。

所以在这个例子中,'nẵng' 是:

n 006E LATIN SMALL LETTER N
ă 0103 LATIN SMALL LETTER A WITH BREVE
◌̃ 0303 COMBINING TILDE
n 006E LATIN SMALL LETTER N
g 0067 LATIN SMALL LETTER G

>>> s = 'nẵng'
>>> s.encode('UTF-8')
b'n\xc4\x83\xcc\x83ng'
>>> from unicodedata import normalize
>>> normalize("NFC", s).encode('UTF-8')
b'n\xe1\xba\xb5ng'
>>> normalize("NFD", s).encode('UTF-8')
b'na\xcc\x86\xcc\x83ng'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接