Python Unicode处理指南:解决乱码与编码难题
简介
Unicode是全球大多数计算机的标准字符编码。它确保文本(包括字母、符号、表情符号和控制字符)在不同设备、平台和数字文档上显示一致,无论使用的操作系统或软件如何。它是互联网和计算机行业的重要组成部分,没有它,互联网将变得更加混乱和难以使用。
Unicode其本身并不是编码,而更像是一个包含地球上几乎所有可能字符的数据库。Unicode包含一个代码点,即其数据库中每个字符的标识符,其值的范围从0到110万,这意味着它几乎不太可能会用完这些独特的代码点。Unicode中的每个代码点都表示为U+n的形式,其中U+表示它是一个Unicode代码点,n是该字符的四到六位十六进制数字。它是比ASCII更为强大的编码系统,后者仅表示128个字符。使用ASCII在全球范围内交换数字文本很困难,因为它是基于美式英语,不支持重音字符。另一方面,Unicode几乎包含15万个字符,并覆盖了地球上每种语言的字符。
随之而来的是对编程语言(如Python)的需求,以便正确处理文本并使软件实现国际化成为可能。Python可用于各种任务,包括电子邮件、服务器和网络,它通过采用Unicode标准来优雅地处理Unicode,并具有处理字符串的方法。
在Python中使用Unicode可能会令人困惑并导致错误。本教程将介绍如何在Python中使用Unicode的基础知识,帮助您避免这些问题。您将使用Python来解释Unicode,使用Python的标准化函数来规范数据,并处理Python中的Unicode错误。
先决条件
要跟随本教程,你需要:
- 在本地或远程服务器上安装Python。如果你尚未设置Python,可以按照我们的教程如何安装Python 3并设置编程环境进行操作。请选择适合你Linux发行版的版本。
- 熟悉基本的Python编程和Python的字符串方法。
- 了解如何使用Python交互式控制台。
步骤1 — 在Python中转换Unicode码点
编码是将数据表示为计算机可读形式的过程。有许多方法可以对数据进行编码,如ASCII、Latin-1等,每种编码都有其优缺点,但最常见的可能是UTF-8。这是一种编码类型,可以在单个字符集中表示来自世界各地的字符。因此,对于处理国际化数据的任何人来说,UTF-8都是必不可少的工具。总的来说,UTF-8对大多数用途来说都是一个很好的选择。它相对高效,并且可以与各种软件配合使用。UTF-8将Unicode代码点转换为计算机可以理解的十六进制字节。换句话说,Unicode是映射,而UTF-8使计算机能够理解该映射。
在Python 3中,默认的字符串编码是UTF-8,这意味着Python字符串中的任何Unicode字符会自动转换为相应的字符。
在这一步中,您将使用Python中的Unicode代码点创建版权符号(©)。首先,在终端中启动Python交互式控制台,并键入以下内容:
>>> s = '\u00A9'
>>> s
在前面的代码中,您使用Unicode代码点\u00A9
创建了一个字符串s
。如前所述,由于Python字符串默认使用UTF-8编码,打印s
的值会自动将其转换为相应的Unicode符号。请注意,代码点前面的\u
是必需的。如果没有它,Python将无法转换该代码点。前面代码的输出返回相应的Unicode符号。
'©'
Python编程语言提供了内置函数来对字符串进行编码和解码。encode()
函数将一个字符串转换为字节字符串。
要证明这一点,打开Python交互控制台,然后输入以下代码。
>>> '?'.encode('utf-8')
这将产生以字节串形式输出的字符。
b'\xf0\x9f\x85\xa5'
请注意,每个字节前面都有一个\x
,表示它是一个十六进制数。
注意:在Windows和Mac上输入特殊Unicode字符的方式不同。在上面的代码和本教程中使用符号的所有代码中,您可以使用Windows中的字符映射实用程序插入这些符号。Macs没有这个功能,所以你最好的选择是从代码示例中复制字符。接下来,您将使用decode()
函数将字节字符串转换回字符串。decode()
函数接受编码类型作为参数。值得一提的是,decode()
函数只能解码字节字符串,这可通过在字符串开头使用字母b
来指定。如果删除b
,则会导致AttributeError
。
在你的控制台中输入:
>>> b'\xf0\x9f\x85\xa5'.decode('utf-8')
代码将返回这样的输出:
'?'
你现在对Python中Unicode解释有了基本的了解。接下来,你将深入学习Python内置的unicodedata
模块,以便对字符串进行高级的Unicode技术处理。
第二步 — 使用Python对Unicode进行标准化
在这一步骤中,您将使用Python对Unicode进行归一化处理。归一化有助于确定使用不同字体书写的两个字符是否相同,在两个具有不同码点但产生相同结果的字符时特别有用。例如,Unicode字符R和ℜ在人眼看来是相同的,因为它们都是字母R,但计算机认为它们是不同的字符。
以下这个代码示例进一步证明了这一点。打开你的Python控制台并键入以下内容:
>>> styled_R = 'ℜ'
>>> normal_R = 'R'
>>> styled_R == normal_R
你将会得到以下结果。
这是文章《如何在Python中处理Unicode》的第2部分(共6部分)。
False
代码输出False
,因为Python字符串不认为这两个字符是相同的。这种区分能力在使用Unicode进行规范化时非常重要。
在Unicode中,一些字符是由两个或更多字符组合而成的。在这种情况下,规范化非常重要,因为它可以保持字符串之间的一致性。为了更好地理解,请打开您的Python控制台并输入以下代码:
>>> s1 = 'hôtel'
>>> s2 = 'ho\u0302tel'
>>> len(s1), len(s2)
在前面的代码中,您创建了一个包含ô
字符的字符串s1
,而在第二行,字符串s2
包含着插入符号字符(^
)的编码点。执行后,代码返回以下输出:
(5, 6)
前面的输出显示,这两个字符串由相同的字符构成,但长度不同,这意味着它们不会相等。在同一个控制台中输入以下内容以进行测试:
>>> s1 == s2
该代码返回以下输出:
False
尽管字符串变量s1
和s2
产生相同的Unicode字符,但是它们的长度不同,因此它们并不相等。
您可以通过使用normalize()
函数来解决此问题,这将是您接下来要做的步骤。
第三步 – 使用NFD、NFC、NFKD和NFKC对Unicode进行规范化
在这一步骤中,您将使用Python的unicodedata
库中的normalize()
函数对Unicode字符串进行标准化,该库在unicodedata
模块中提供了字符查找和标准化的功能。normalize()
函数的第一个参数可以接受一个标准化形式,第二个参数是要进行标准化的字符串。Unicode有四种标准化形式可供选择:NFD、NFC、NFKD和NFKC。
使用NFD规范将一个字符拆分为多个组合字符。这样可以使您的文本对重音不敏感,在搜索和排序时非常有用。您可以通过将字符串编码为字节来实现这一点。
打开您的控制台,然后输入以下内容:
from unicodedata import normalize
>>> s1 = 'hôtel'
>>> s2 = 'ho\u0302tel'
>>> s1_nfd = normalize('NFD', s1)
>>> len(s1), len(s1_nfd)
该代码生成以下输出:
(5, 6)
正如示例所示,将字符串s1
进行规范化会使其长度增加一个字符。这是因为ô
符号被拆分为两个字符o
和ˆ
,您可以通过使用以下代码进行确认:
>>> s1.encode(), s1_nfd.encode()
经过编码规范化字符串后的结果显示,字符o
被从字符串s1_nfd
的字符ˆ
分离出来。
这是文章《如何在Python中处理Unicode》的第3部分(共6部分)。
(b'h\xc3\xb4tel', b'ho\xcc\x82tel')
NFC标准化形式首先将字符进行分解,然后使用任何可用的组合字符进行重新组合。由于NFC将字符串组合为可能最短的输出形式,W3C建议在网络上使用NFC。键盘输入默认返回组合字符串,因此在这种情况下使用NFC是一个好主意。
举一个例子,将以下内容输入到你的交互式控制台中。
>>> from unicodedata import normalize
>>> s2_nfc = normalize('NFC', s2)
>>> len(s2), len(s2_nfc)
这段代码的输出结果如下:
(6, 5)
在这个例子中,将字符串s2
规范化会使其长度减少一个字符。您可以通过在交互式控制台中运行以下代码来确认这一点。
>>> s2.encode(), s2_nfc.encode()
代码的输出是:
(b'ho\xcc\x82tel', b'h\xc3\xb4tel')
结果显示,字符“o”和“ˆ”合并成了一个“ô”字符。
NFKD和NFKC规范形式用于“严格”归一化,并可用于与Unicode字符串的搜索和模式匹配相关的各种问题。NFKD和NFKC中的“K”代表兼容性。
NFD和NFC标准化形式对字符进行分解,而NFKD和NFKC对不相似但等价的字符进行兼容性分解,去除任何格式上的区别。例如,字符串②①
与21
不相似,但它们都代表同样的值。NFKC和NFKD标准化形式去除了字符中的格式(在本例中是数字周围的圆圈),以提供最简化的形式。
以下示例演示了NFD和NFKD规范化形式之间的区别。打开你的Python交互控制台并输入以下内容:
>>> s1 = '2⁵ô'
>>> from unicodedata import normalize
>>> normalize('NFD', s1), normalize('NFKD', s1)
你将获得以下的输出结果。
('2⁵ô', '25ô')
输出结果显示NFD形式无法分解字符串s1
中的指数字符,但NFKD去除了指数格式并替换了兼容字符(在本例中是指数5)为其等价的数字5。请记住,NFD和NFKD规范化形式仍在分解字符,所以像前面NFD示例中所看到的,ô
字符的长度应增加1个。您可以运行以下代码来确认这一点。
>>> len(normalize('NFD', s1)), len(normalize('NFKD', s1))
代码会返回以下内容:
这是文章《如何在Python中处理Unicode》的第4部分(共6部分)。
NFKC规范化形式的工作方式类似,但是它合并字符而不是分解字符。在相同的Python控制台中输入以下内容:
>>> normalize('NFC', s1), normalize('NFKC', s1)
代码返回以下结果:
('2⁵ô', '25ô')
由于NFKC遵循组合方法,所以您应该预期在分解的情况下,ø字符的字符串将缩短一个字符,而不是增加一个字符。您可以通过运行以下代码行来确认这一点:
>>> len(normalize('NFC', s1)), len(normalize('NFKC', s1))
这将返回以下输出结果。
(3, 3)
通过执行前面的步骤,您将对规范化形式及其之间的差异有了实际操作的了解。在下一步中,您将解决Python中的Unicode错误。
步骤4 – 解决Python中的Unicode错误
在处理Python中的Unicode时,可能会出现两种类型的Unicode错误,即UnicodeEncodeError
和UnicodeDecodeError
。虽然这些Unicode错误可能会令人困惑,但是它们是可以解决的,您将在此步骤中修复这两个错误。
解决UnicodeEncodeError问题
使用Unicode进行编码是将Unicode字符串转换为特定编码的字节的过程。当尝试对包含无法在指定编码中表示的字符的字符串进行编码时,会出现UnicodeEncodeError
。
为了创建这个错误,您需要编码一个包含不属于ASCII字符集的字符的字符串。
打开您的控制台,然后输入以下内容:
>>> ascii_supported = '\u0041'
>>> ascii_supported.encode('ascii')
以下是您的输出:
b'A'
接着,请输入以下内容:
>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('utf-8')
您将获得以下结果:
b'\xef\xac\x86'
最后,请输入以下内容。
>>> ascii_unsupported.encode('ascii')
当你运行这段代码时,然而,你会得到以下错误:
这是文章《如何在Python中处理Unicode》的第5部分(共6部分)。
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
UnicodeEncodeError: ‘ascii’ codec can’t encode character ‘\ufb06’ in position 0: ordinal not in range(128)
ASCII字符集是有限的,当Python遇到ASCII字符集中不存在的字符时,会抛出错误。由于ASCII字符集无法识别代码点\ufb06
,Python会返回错误信息,指出ASCII只有128个字符,而该代码点的十进制等效值不在该范围内。
你可以通过在编码函数encode()
中使用errors
参数来处理UnicodeEncodeError
。errors
参数有三个可选值:ignore
、replace
和xmlcharrefreplace
。
打开你的控制台,并输入以下内容:
>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('ascii', errors='ignore')
你将会得到以下的输出:
b''
接下来,请输入以下内容:
>>> ascii_unsupported.encode('ascii', errors='replace')
输出将会是:
b'?'
最后,请将以下内容输入:
>>> ascii_unsupported.encode('ascii', errors='xmlcharrefreplace')
输出是:
b'st'
在每种情况下,Python都不会抛出错误。
如前面的例子所示,ignore
会跳过不能编码的字符,replace
会用问号代替字符,而xmlcharrefreplace
则使用XML实体替换无法编码的字符。
解决UnicodeDecodeError的方法
当尝试解码一个包含无法在指定编码中表示的字符的字符串时,会出现UnicodeDecodeError
。
要创建这个错误,你将尝试将字节字符串解码为无法解码的编码。
打开你的控制台,然后输入以下内容:
>>> iso_supported = '§'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8')
你将收到以下错误信息:
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xa7 in position 0: invalid start byte
如果您遇到此错误,可以在 `decode()` 函数中使用 `errors` 参数来帮助您解码字符串。`errors` 参数可以接受两个值:`ignore` 和 `replace`。
为了证明这一点,请打开您的 Python 控制台并输入以下代码:
>>> iso_supported = '§A'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8', errors='replace')
您的输出将是:
'A'
请随后输入以下内容:
>>> b.decode('utf-8', errors='ignore')
您将得到以下输出:
'A'
在前面的例子中,使用 `decode()` 函数中的 `replace` 值会添加一个 “ 字符,而使用 `ignore` 则会在解码器(本例中是 `utf-8`)无法解码字节时返回空值。
在解码任何字符串时,请注意不能假设其编码方式。要正确解码任何字符串,您必须知道它是如何编码的。
结论
本文介绍了在 Python 中使用 Unicode 的基础知识。您可以对字符串进行编码和解码操作,使用 NFD、NFC、NFKD 和 NFKC 来规范化数据,并解决 Unicode 错误。您还可以在排序和搜索场景中使用规范化形式。这些技术将帮助您使用 Python 处理 Unicode 问题。作为下一步,您可以阅读 `unicodedata` 模块的文档,了解该模块提供的其他功能。要继续探索如何使用 Python 进行编程,请阅读我们的教程系列《如何在 Python 3 中编码》。