如何在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 installed locally or on a remote server. If you do not already have Python set up, you can do so by following our tutorial How To Install Python 3 and Set Up a Programming Environment. Choose the version that is appropriate for your Linux distribution.
  • Familiarity with basic Python programming and Python’s string methods
  • Knowledge of how to work with the Python Interactive Console

步骤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符号。

Output

‘©’

Python编程语言提供了内置函数来对字符串进行编码和解码。encode()函数将一个字符串转换为字节字符串。

要证明这一点,打开Python交互控制台,然后输入以下代码。

>>> '?'.encode('utf-8')

这将产生以字节串形式输出的字符。

Output

b’\xf0\x9f\x85\xa5′

请注意,每个字节前面都有一个\x,表示它是一个十六进制数。

Note

注意:在Windows和Mac上输入特殊Unicode字符的方式不同。在上面的代码和本教程中使用符号的所有代码中,您可以使用Windows中的字符映射实用程序插入这些符号。Macs没有这个功能,所以你最好的选择是从代码示例中复制字符。

接下来,您将使用decode()函数将字节字符串转换回字符串。decode()函数接受编码类型作为参数。值得一提的是,decode()函数只能解码字节字符串,这可通过在字符串开头使用字母b来指定。如果删除b,则会导致AttributeError。

在你的控制台中输入:

>>> b'\xf0\x9f\x85\xa5'.decode('utf-8')

代码将返回这样的输出:

Output

‘?’

你现在对Python中Unicode解释有了基本的了解。接下来,你将深入学习Python内置的unicodedata模块,以便对字符串进行高级的Unicode技术处理。

第二步 — 使用Python对Unicode进行标准化

在这一步骤中,您将使用Python对Unicode进行归一化处理。归一化有助于确定使用不同字体书写的两个字符是否相同,在两个具有不同码点但产生相同结果的字符时特别有用。例如,Unicode字符R和ℜ在人眼看来是相同的,因为它们都是字母R,但计算机认为它们是不同的字符。

以下这个代码示例进一步证明了这一点。打开你的Python控制台并键入以下内容:

>>> styled_R = 'ℜ'
>>> normal_R = 'R'
>>> styled_R == normal_R

你将会得到以下结果。 (Nǐ huì .)

Output

False

代码打印出False作为输出,因为Python字符串不认为这两个字符是相同的。这种区分能力是在使用Unicode时进行规范化时非常重要的原因。

在Unicode中,一些字符是由两个或更多字符组合而成的。在这种情况下,规范化非常重要,因为它可以保持字符串之间的一致性。为了更好地理解,请打开您的Python控制台并键入以下代码。

>>> s1 =  'hôtel'
>>> s2 = 'ho\u0302tel'
>>> len(s1), len(s2)

在前面的代码中,你创建了一个包含ô字符的字符串s1,而在第二行,字符串s2包含着插入符号字符(^)的编码点。在执行后,代码返回以下输出:

Output

(5, 6)

前面的输出显示,这两个字符串由相同的字符构成,但长度不同,这意味着它们不会相等。在同一个控制台中输入以下内容以进行测试:

>>> s1 == s2

该代码返回以下输出:

Output

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)

该代码生成以下输出:

Output

(5, 6)

正如示例所示,将字符串s1进行规范化会使其长度增加一个字符。这是因为ô符号被拆分为两个字符o和ˆ,您可以通过使用以下代码进行确认:

>>> s1.encode(), s1_nfd.encode()

经过编码正规化字符串后的结果显示,字符 o 被从字符串 s1_nfd 的字符 ˆ 分离出来。

Output

(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)

这段代码的输出结果如下:

Output

(6, 5)

在这个例子中,将字符串s2规范化会使其长度减少一个字符。您可以通过在交互式控制台中运行以下代码来确认这一点。

>>> s2.encode(), s2_nfc.encode()

代码的输出是:

Output

(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)

你将获得以下的输出结果。

Output

(‘2⁵ô’, ’25ô’)

输出结果显示NFD形式无法分解字符串s1中的指数字符,但NFKD去除了指数格式并替换了兼容字符(在本例中是指数5)为其等价的数字5。请记住,NFD和NFKD规范化形式仍在分解字符,所以像前面NFD示例中所看到的,ô字符的长度应增加1个。您可以运行以下代码来确认这一点。

>>> len(normalize('NFD', s1)), len(normalize('NFKD', s1))

代码会返回以下内容:

Output

(4, 4)

NFKC规范化形式的工作方式类似,但是是合并字符而不是分解字符。在相同的Python控制台中输入以下内容:

>>> normalize('NFC', s1), normalize('NFKC', s1)

代码返回以下结果:

Output

(‘2⁵ô’, ’25ô’)

由于NFKC遵循组合方法,所以您应该预期在分解的情况下,ø字符的字符串将缩短一个字符,而不是增加一个字符。您可以通过运行以下代码行来确认这一点:

>>> len(normalize('NFC', s1)), len(normalize('NFKC', s1))

这将返回以下输出结果。

Output

(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')

以下是你的输出:

Output

b’A’

接着,请输入以下内容:

>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('utf-8')

您将获得以下结果:

Output

b’\xef\xac\x86′

最后,请输入以下内容。

>>> ascii_unsupported.encode('ascii')

当你运行这段代码时,然而,你会得到以下错误:

Output

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')

你将会得到以下的输出

Output

b”

接下来,请输入以下内容:

>>> >>> ascii_unsupported.encode('ascii', errors='replace')

输出将会是:

Output

b’?’

最后,请将以下内容输入:

>>> ascii_unsupported.encode('ascii', errors='xmlcharrefreplace')

输出是:

Output

b’&#64262;’

在每种情况下,Python不会抛出错误。

如前面的例子所示,ignore 跳过不能编码的字符,replace 用问号代替字符,而 xmlcharrefreplace 使用 XML 实体替换无法编码的字符。

解决UnicodeDecodeError的方法

当尝试解码一个包含无法在指定编码中表示的字符的字符串时,会出现UnicodeDecodeError。

要创建这个错误,你将尝试将字节字符串解码为无法解码的编码。

打开你的控制台,然后输入以下内容:

>>> iso_supported = '§'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8')

你将收到以下错误信息:

Output

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')

你的输出将是:

Output

‘�A’

请随后输入以下内容:

>>> b.decode('utf-8', errors='ignore')

您将输出为:

Output

‘A’

在先前的例子中,使用decode()函数中的替换值增加了一个�字符,并且使用ignore在解码器(在这种情况下是utf-8)无法解码字节时返回了空值。

在解码任何字符串时,请注意不能假设其编码方式。要解码任何字符串,你必须知道它是如何编码的。

结论

本文介绍了如何在Python中使用Unicode的基础知识。您可以对字符串进行编码和解码操作,使用NFD、NFC、NFKD和NFKC来规范化数据,并解决Unicode错误。您还可以在排序和搜索场景中使用规范化形式。这些技术将帮助您使用Python处理Unicode问题。作为下一步,您可以阅读unicodedata模块的文档,了解该模块提供的其他功能。要继续探索如何使用Python进行编程,请阅读我们的教程系列《如何在Python 3中编码》。

发表回复 0

Your email address will not be published. Required fields are marked *


广告
将在 10 秒后关闭
bannerAds