“Python/docs/3.9/howto/unicode”的版本间差异

来自菜鸟教程
Python/docs/3.9/howto/unicode
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:Unicode HOWTO — Python 文档}}
 
<div id="unicode-howto" class="section">
 
<div id="unicode-howto" class="section">
  
第4行: 第5行:
 
= Unicode HOWTO =
 
= Unicode HOWTO =
  
; Release
+
; 发布
 
: 1.12
 
: 1.12
  
This HOWTO discusses Python's support for the Unicode specification
+
HOWTO 讨论了 Python 对用于表示文本数据的 Unicode 规范的支持,并解释了人们在尝试使用 Unicode 时经常遇到的各种问题。
for representing textual data, and explains various problems that
 
people commonly encounter when trying to work with Unicode.
 
  
 
<div id="introduction-to-unicode" class="section">
 
<div id="introduction-to-unicode" class="section">
  
== Introduction to Unicode ==
+
== Unicode 简介 ==
  
 
<div id="definitions" class="section">
 
<div id="definitions" class="section">
  
=== Definitions ===
+
=== 定义 ===
  
Today's programs need to be able to handle a wide variety of
+
今天的程序需要能够处理各种各样的字符。 应用程序通常是国际化的,以各种用户可选择的语言显示消息和输出; 同一程序可能需要以英语、法语、日语、希伯来语或俄语输出错误消息。 Web 内容可以用这些语言中的任何一种编写,还可以包含各种表情符号。 Python 的字符串类型使用 Unicode 标准来表示字符,这让 Python 程序可以处理所有这些不同的可能字符。
characters. Applications are often internationalized to display
 
messages and output in a variety of user-selectable languages; the
 
same program might need to output an error message in English, French,
 
Japanese, Hebrew, or Russian. Web content can be written in any of
 
these languages and can also include a variety of emoji symbols.
 
Python's string type uses the Unicode Standard for representing
 
characters, which lets Python programs work with all these different
 
possible characters.
 
  
Unicode (https://www.unicode.org/) is a specification that aims to
+
Unicode (https://www.unicode.org/) 是一种规范,旨在列出人类语言使用的每个字符,并为每个字符提供自己的唯一代码。 Unicode 规范不断修订和更新,以添加新的语言和符号。
list every character used by human languages and give each character
 
its own unique code. The Unicode specifications are continually
 
revised and updated to add new languages and symbols.
 
  
A '''character''' is the smallest possible component of a text. 'A', 'B', 'C',
+
'''character''' 是文本的最小可能组成部分。 'A''B''C' 等都是不同的字符。 'È' 'Í' 也是如此。 字符因您所谈论的语言或上下文而异。 例如,“罗马数字一”有一个字符“Ⅰ”,它与大写字母“I”分开。 它们通常看起来相同,但这是两个具有不同含义的不同字符。
etc., are all different characters. So are 'È' and 'Í'. Characters vary
 
depending on the language or context you're talking
 
about. For example, there's a character for &quot;Roman Numeral One&quot;, 'Ⅰ', that's
 
separate from the uppercase letter 'I'. They'll usually look the same,
 
but these are two different characters that have different meanings.
 
  
The Unicode standard describes how characters are represented by
+
Unicode 标准描述了字符如何由 '''码位''' 表示。 代码点值是 0 0x10FFFF 范围内的整数(大约 110 万个值, [https://www.unicode.org/versions/latest/#Summary 分配的实际数字] 小于该值)。 在标准和本文档中,代码点使用符号 <code>U+265E</code> 表示值为 <code>0x265e</code>(十进制为 9,822)的字符。
'''code points'''. A code point value is an integer in the range 0 to
 
0x10FFFF (about 1.1 million values, the
 
[https://www.unicode.org/versions/latest/#Summary actual number assigned]
 
is less than that). In the standard and in this document, a code point is written
 
using the notation <code>U+265E</code> to mean the character with value
 
<code>0x265e</code> (9,822 in decimal).
 
  
The Unicode standard contains a lot of tables listing characters and
+
Unicode 标准包含许多列出字符及其相应代码点的表格:
their corresponding code points:
 
  
 
<div class="highlight-none notranslate">
 
<div class="highlight-none notranslate">
第56行: 第32行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>0061    'a'; LATIN SMALL LETTER A
+
<pre class="none">0061    'a'; LATIN SMALL LETTER A
 
0062    'b'; LATIN SMALL LETTER B
 
0062    'b'; LATIN SMALL LETTER B
 
0063    'c'; LATIN SMALL LETTER C
 
0063    'c'; LATIN SMALL LETTER C
第75行: 第51行:
  
 
</div>
 
</div>
Strictly, these definitions imply that it's meaningless to say 'this is
+
严格来说,这些定义意味着说“这是字符 <code>U+265E</code>”是没有意义的。 <code>U+265E</code> 是一个码位,代表某个特定的字符; 在本例中,它代表字符“BLACK CHESS KNIGHT”、“♞”。 在非正式环境中,有时会忘记代码点和字符之间的这种区别。
character <code>U+265E</code>'. <code>U+265E</code> is a code point, which represents some particular
 
character; in this case, it represents the character 'BLACK CHESS KNIGHT',
 
'♞'. In
 
informal contexts, this distinction between code points and characters will
 
sometimes be forgotten.
 
  
A character is represented on a screen or on paper by a set of graphical
+
字符在屏幕或纸张上由一组称为 '''字形''' 的图形元素表示。 例如,大写字母 A 的字形是两个对角线笔画和一个水平笔画,但具体细节取决于所使用的字体。 大多数 Python 代码不需要担心字形; 找出要显示的正确字形通常是 GUI 工具包或终端的字体渲染器的工作。
elements that's called a '''glyph'''. The glyph for an uppercase A, for example,
 
is two diagonal strokes and a horizontal stroke, though the exact details will
 
depend on the font being used. Most Python code doesn't need to worry about
 
glyphs; figuring out the correct glyph to display is generally the job of a GUI
 
toolkit or a terminal's font renderer.
 
  
  
第93行: 第59行:
 
<div id="encodings" class="section">
 
<div id="encodings" class="section">
  
=== Encodings ===
+
=== 编码 ===
  
To summarize the previous section: a Unicode string is a sequence of
+
总结上一节:Unicode 字符串是一系列代码点,它们是从 0 <code>0x10FFFF</code>(十进制 1,114,111)的数字。 这个代码点序列需要在内存中表示为一组'''代码单元''',然后将'''代码单元'''映射到8位字节。 将 Unicode 字符串转换为字节序列的规则称为 '''字符编码''' ,或简称为 '''编码'''
code points, which are numbers from 0 through <code>0x10FFFF</code> (1,114,111
 
decimal). This sequence of code points needs to be represented in
 
memory as a set of '''code units''', and '''code units''' are then mapped
 
to 8-bit bytes. The rules for translating a Unicode string into a
 
sequence of bytes are called a '''character encoding''', or just
 
an '''encoding'''.
 
  
The first encoding you might think of is using 32-bit integers as the
+
您可能想到的第一种编码是使用 32 位整数作为代码单元,然后使用 CPU 32 位整数的表示。 在此表示中,字符串“Python”可能如下所示:
code unit, and then using the CPU's representation of 32-bit integers.
 
In this representation, the string &quot;Python&quot; might look like this:
 
  
 
<div class="highlight-none notranslate">
 
<div class="highlight-none notranslate">
第111行: 第69行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>  P          y          t          h          o          n
+
<pre class="none">  P          y          t          h          o          n
 
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
 
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
 
   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23</pre>
 
   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23</pre>
第118行: 第76行:
  
 
</div>
 
</div>
This representation is straightforward but using it presents a number of
+
这种表示很简单,但使用它会带来许多问题。
problems.
 
  
# It's not portable; different processors order the bytes differently.
+
# 它不便携; 不同的处理器以不同的方式对字节进行排序。
# It's very wasteful of space. In most texts, the majority of the code points are less than 127, or less than 255, so a lot of space is occupied by <code>0x00</code> bytes. The above string takes 24 bytes compared to the 6 bytes needed for an ASCII representation. Increased RAM usage doesn't matter too much (desktop computers have gigabytes of RAM, and strings aren't usually that large), but expanding our usage of disk and network bandwidth by a factor of 4 is intolerable.
+
# 非常浪费空间。 在大多数文本中,大多数代码点小于 127,或者小于 255,因此 <code>0x00</code> 字节占用了大量空间。 与 ASCII 表示所需的 6 个字节相比,上述字符串需要 24 个字节。 RAM 使用量的增加并不重要(台式计算机有 GB 的 RAM,而字符串通常不会那么大),但是将我们对磁盘和网络带宽的使用量增加 4 倍是无法忍受的。
# It's not compatible with existing C functions such as <code>strlen()</code>, so a new family of wide string functions would need to be used.
+
# 它与现有的 C 函数(如 <code>strlen()</code>)不兼容,因此需要使用新的宽字符串函数系列。
  
Therefore this encoding isn't used very much, and people instead choose other
+
因此这种编码使用的不多,人们转而选择其他更高效、更方便的编码,例如UTF-8。
encodings that are more efficient and convenient, such as UTF-8.
 
  
UTF-8 is one of the most commonly used encodings, and Python often
+
UTF-8 是最常用的编码之一,Python 通常默认使用它。 UTF 代表“Unicode 转换格式”,'8' 表示在编码中使用 8 位值。 (也有 UTF-16 UTF-32 编码,但它们的使用频率不如 UTF-8。)UTF-8 使用以下规则:
defaults to using it. UTF stands for &quot;Unicode Transformation Format&quot;,
 
and the '8' means that 8-bit values are used in the encoding. (There
 
are also UTF-16 and UTF-32 encodings, but they are less frequently
 
used than UTF-8.) UTF-8 uses the following rules:
 
  
# If the code point is &lt; 128, it's represented by the corresponding byte value.
+
# 如果代码点 &lt; 128,则由相应的字节值表示。
# If the code point is &gt;= 128, it's turned into a sequence of two, three, or four bytes, where each byte of the sequence is between 128 and 255.
+
# 如果代码点 &gt;= 128,则将其转换为两个、三个或四个字节的序列,其中该序列的每个字节介于 128 255 之间。
  
UTF-8 has several convenient properties:
+
UTF-8 有几个方便的特性:
  
# It can handle any Unicode code point.
+
# 它可以处理任何 Unicode 代码点。
# A Unicode string is turned into a sequence of bytes that contains embedded zero bytes only where they represent the null character (U+0000). This means that UTF-8 strings can be processed by C functions such as <code>strcpy()</code> and sent through protocols that can't handle zero bytes for anything other than end-of-string markers.
+
# Unicode 字符串被转换为一个字节序列,其中仅在它们表示空字符 (U+0000) 的地方包含嵌入的零字节。 这意味着 UTF-8 字符串可以由 C 函数(如 <code>strcpy()</code>)处理,并通过除了字符串结束标记之外无法处理零字节的协议发送。
# A string of ASCII text is also valid UTF-8 text.
+
# 一串 ASCII 文本也是有效的 UTF-8 文本。
# UTF-8 is fairly compact; the majority of commonly used characters can be represented with one or two bytes.
+
# UTF-8 相当紧凑; 大多数常用字符可以用一两个字节表示。
# If bytes are corrupted or lost, it's possible to determine the start of the next UTF-8-encoded code point and resynchronize. It's also unlikely that random 8-bit data will look like valid UTF-8.
+
# 如果字节损坏或丢失,则可以确定下一个 UTF-8 编码代码点的开始并重新同步。 随机 8 位数据也不太可能看起来像有效的 UTF-8。
# UTF-8 is a byte oriented encoding. The encoding specifies that each character is represented by a specific sequence of one or more bytes. This avoids the byte-ordering issues that can occur with integer and word oriented encodings, like UTF-16 and UTF-32, where the sequence of bytes varies depending on the hardware on which the string was encoded.
+
# UTF-8 是一种面向字节的编码。 编码指定每个字符由一个或多个字节的特定序列表示。 这避免了在整数和面向字的编码(如 UTF-16 UTF-32)中可能出现的字节顺序问题,其中字节序列因编码字符串的硬件而异。
  
  
第150行: 第102行:
 
<div id="references" class="section">
 
<div id="references" class="section">
  
=== References ===
+
=== 参考 ===
  
The [https://www.unicode.org Unicode Consortium site] has character charts, a
+
[https://www.unicode.org Unicode Consortium 站点] 有字符表、词汇表和 Unicode 规范的 PDF 版本。 为一些困难的阅读做好准备。 [https://www.unicode.org/history/ 网站上还提供了 Unicode 起源和发展的年表]
glossary, and PDF versions of the Unicode specification. Be prepared for some
 
difficult reading. [https://www.unicode.org/history/ A chronology] of the
 
origin and development of Unicode is also available on the site.
 
  
On the Computerphile Youtube channel, Tom Scott briefly
+
Computerphile Youtube 频道上,Tom Scott 简要 [https://www.youtube.com/watch?v=MijmeoH9LT4 讨论了 Unicode UTF-8] 的历史(9 分 36 秒)。
[https://www.youtube.com/watch?v=MijmeoH9LT4 discusses the history of Unicode and UTF-8]
 
(9 minutes 36 seconds).
 
  
To help understand the standard, Jukka Korpela has written [http://jkorpela.fi/unicode/guide.html an introductory
+
为了帮助理解该标准,Jukka Korpela 编写了 [http://jkorpela.fi/unicode/guide.html 介绍性指南] 以阅读 Unicode 字符表。
guide] to reading the
 
Unicode character tables.
 
  
Another [https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/ good introductory article]
+
另一篇 [https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/ 好的介绍性文章] Joel Spolsky 撰写。 如果这篇介绍没有让你清楚,你应该在继续之前尝试阅读这篇替代文章。
was written by Joel Spolsky.
 
If this introduction didn't make things clear to you, you should try
 
reading this alternate article before continuing.
 
  
Wikipedia entries are often helpful; see the entries for &quot;[https://en.wikipedia.org/wiki/Character_encoding character encoding]&quot; and [https://en.wikipedia.org/wiki/UTF-8 UTF-8], for example.
+
维基百科条目通常很有帮助; 例如,参见“[https://en.wikipedia.org/wiki/Character_encoding 字符编码]”和[https://en.wikipedia.org/wiki/UTF-8 UTF-8]的条目。
  
  
第178行: 第120行:
 
<div id="python-s-unicode-support" class="section">
 
<div id="python-s-unicode-support" class="section">
  
== Python's Unicode Support ==
+
== Python Unicode 支持 ==
  
Now that you've learned the rudiments of Unicode, we can look at Python's
+
现在您已经了解了 Unicode 的基础知识,我们可以看看 Python Unicode 特性。
Unicode features.
 
  
 
<div id="the-string-type" class="section">
 
<div id="the-string-type" class="section">
  
=== The String Type ===
+
=== 字符串类型 ===
  
Since Python 3.0, the language's [[../../library/stdtypes#str|<code>str</code>]] type contains Unicode
+
Python 3.0 开始,语言的 [[../../library/stdtypes#str|str]] 类型包含 Unicode 字符,这意味着使用 <code>&quot;unicode rocks!&quot;</code><code>'unicode rocks!'</code> 或三引号字符串语法创建的任何字符串都存储为 Unicode。
characters, meaning any string created using <code>&quot;unicode rocks!&quot;</code>, <code>'unicode rocks!'</code>, or the triple-quoted string syntax is stored as Unicode.
 
  
The default encoding for Python source code is UTF-8, so you can simply
+
Python 源代码的默认编码是 UTF-8,因此您可以简单地在字符串文字中包含一个 Unicode 字符:
include a Unicode character in a string literal:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第197行: 第136行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>try:
+
<syntaxhighlight lang="python3">try:
 
     with open('/tmp/input.txt', 'r') as f:
 
     with open('/tmp/input.txt', 'r') as f:
 
         ...
 
         ...
 
except OSError:
 
except OSError:
 
     # 'File not found' error message.
 
     # 'File not found' error message.
     print(&quot;Fichier non trouvé&quot;)</pre>
+
     print("Fichier non trouvé")</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Side note: Python 3 also supports using Unicode characters in identifiers:
+
旁注:Python 3 还支持在标识符中使用 Unicode 字符:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第213行: 第152行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>répertoire = &quot;/tmp/records.log&quot;
+
<syntaxhighlight lang="python3">répertoire = "/tmp/records.log"
with open(répertoire, &quot;w&quot;) as f:
+
with open(répertoire, "w") as f:
     f.write(&quot;test\n&quot;)</pre>
+
     f.write("test\n")</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you can't enter a particular character in your editor or want to
+
如果您无法在编辑器中输入特定字符或出于某种原因想要仅保留源代码 ASCII,您还可以在字符串文字中使用转义序列。 (根据您的系统,您可能会看到实际的大写字母 - delta 字形而不是 au 转义符。)
keep the source code ASCII-only for some reason, you can also use
 
escape sequences in string literals. (Depending on your system,
 
you may see the actual capital-delta glyph instead of a u escape.)
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第229行: 第165行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; &quot;\N{GREEK CAPITAL LETTER DELTA}&quot; # Using the character name
+
<syntaxhighlight lang="python3">>>> "\N{GREEK CAPITAL LETTER DELTA}" # Using the character name
 
'\u0394'
 
'\u0394'
&gt;&gt;&gt; &quot;\u0394&quot;                         # Using a 16-bit hex value
+
>>> "\u0394"                         # Using a 16-bit hex value
 
'\u0394'
 
'\u0394'
&gt;&gt;&gt; &quot;\U00000394&quot;                     # Using a 32-bit hex value
+
>>> "\U00000394"                     # Using a 32-bit hex value
'\u0394'</pre>
+
'\u0394'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
In addition, one can create a string using the [[../../library/stdtypes#bytes|<code>decode()</code>]] method of
+
此外,可以使用 [[../../library/stdtypes#bytes|bytes]] [[../../library/stdtypes#bytes|decode()]] 方法创建字符串。 此方法采用 ''encoding'' 参数,例如 <code>UTF-8</code>,以及可选的 ''errors'' 参数。
[[../../library/stdtypes#bytes|<code>bytes</code>]]. This method takes an ''encoding'' argument, such as <code>UTF-8</code>,
 
and optionally an ''errors'' argument.
 
  
The ''errors'' argument specifies the response when the input string can't be
+
''errors'' 参数指定无法根据编码规则转换输入字符串时的响应。 此参数的合法值为 <code>'strict'</code>(引发 [[../../library/exceptions#UnicodeDecodeError|UnicodeDecodeError]] 异常)、<code>'replace'</code>(使用 <code>U+FFFD</code><code>REPLACEMENT CHARACTER</code>)、<code>'ignore'</code>(仅将字符从 Unicode 结果中删除),或 <code>'backslashreplace'</code>(插入 <code>\xNN</code> 转义序列)。 以下示例显示了差异:
converted according to the encoding's rules. Legal values for this argument are
 
<code>'strict'</code> (raise a [[../../library/exceptions#UnicodeDecodeError|<code>UnicodeDecodeError</code>]] exception), <code>'replace'</code> (use
 
<code>U+FFFD</code>, <code>REPLACEMENT CHARACTER</code>), <code>'ignore'</code> (just leave the
 
character out of the Unicode result), or <code>'backslashreplace'</code> (inserts a
 
<code>\xNN</code> escape sequence).
 
The following examples show the differences:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第255行: 第183行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; b'\x80abc'.decode(&quot;utf-8&quot;, &quot;strict&quot;)   
+
<syntaxhighlight lang="python3">>>> b'\x80abc'.decode("utf-8", "strict")   
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
 
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
 
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
 
   invalid start byte
 
   invalid start byte
&gt;&gt;&gt; b'\x80abc'.decode(&quot;utf-8&quot;, &quot;replace&quot;)
+
>>> b'\x80abc'.decode("utf-8", "replace")
 
'\ufffdabc'
 
'\ufffdabc'
&gt;&gt;&gt; b'\x80abc'.decode(&quot;utf-8&quot;, &quot;backslashreplace&quot;)
+
>>> b'\x80abc'.decode("utf-8", "backslashreplace")
 
'\\x80abc'
 
'\\x80abc'
&gt;&gt;&gt; b'\x80abc'.decode(&quot;utf-8&quot;, &quot;ignore&quot;)
+
>>> b'\x80abc'.decode("utf-8", "ignore")
'abc'</pre>
+
'abc'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Encodings are specified as strings containing the encoding's name. Python
+
编码被指定为包含编码名称的字符串。 Python 有大约 100 种不同的编码; 有关列表,请参阅 [[../../library/codecs#standard-encodings|标准编码]] 中的 Python 库参考。 一些编码有多个名称; 例如,<code>'latin-1'</code><code>'iso_8859_1'</code> <code>'8859</code>' 都是相同编码的同义词。
comes with roughly 100 different encodings; see the Python Library Reference at
 
[[../../library/codecs#standard-encodings|<span class="std std-ref">Standard Encodings</span>]] for a list. Some encodings have multiple names; for
 
example, <code>'latin-1'</code>, <code>'iso_8859_1'</code> and <code>'8859</code>' are all synonyms for
 
the same encoding.
 
  
One-character Unicode strings can also be created with the [[../../library/functions#chr|<code>chr()</code>]]
+
也可以使用 [[../../library/functions#chr|chr()]] 内置函数创建单字符 Unicode 字符串,该函数接受整数并返回包含相应代码点的长度为 1 的 Unicode 字符串。 反向操作是内置的 [[../../library/functions#ord|ord()]] 函数,它接受一个字符的 Unicode 字符串并返回代码点值:
built-in function, which takes integers and returns a Unicode string of length 1
 
that contains the corresponding code point. The reverse operation is the
 
built-in [[../../library/functions#ord|<code>ord()</code>]] function that takes a one-character Unicode string and
 
returns the code point value:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第286行: 第206行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; chr(57344)
+
<syntaxhighlight lang="python3">>>> chr(57344)
 
'\ue000'
 
'\ue000'
&gt;&gt;&gt; ord('\ue000')
+
>>> ord('\ue000')
57344</pre>
+
57344</syntaxhighlight>
  
 
</div>
 
</div>
第298行: 第218行:
 
<div id="converting-to-bytes" class="section">
 
<div id="converting-to-bytes" class="section">
  
=== Converting to Bytes ===
+
=== 转换为字节 ===
  
The opposite method of [[../../library/stdtypes#bytes|<code>bytes.decode()</code>]] is [[../../library/stdtypes#str|<code>str.encode()</code>]],
+
[[../../library/stdtypes#bytes|bytes.decode()]] 的相反方法是 [[../../library/stdtypes#str|str.encode()]],它返回 Unicode 字符串的 [[../../library/stdtypes#bytes|bytes]] 表示,以请求的 ''编码]编码''
which returns a [[../../library/stdtypes#bytes|<code>bytes</code>]] representation of the Unicode string, encoded in the
 
requested ''encoding''.
 
  
The ''errors'' parameter is the same as the parameter of the
+
''errors'' 参数与 [[../../library/stdtypes#bytes|decode()]] 方法的参数相同,但支持更多可能的处理程序。 除了 <code>'strict'</code><code>'ignore'</code> <code>'replace'</code>(在这种情况下插入一个问号而不是不可编码的字符),还有 <code>'xmlcharrefreplace'</code>(插入 XML 字符引用)、<code>backslashreplace</code>(插入 <code>\uNNNN</code> 转义序列)和 <code>namereplace</code>(插入 <code>\N{...}</code> 转义序列)。
[[../../library/stdtypes#bytes|<code>decode()</code>]] method but supports a few more possible handlers. As well as
 
<code>'strict'</code>, <code>'ignore'</code>, and <code>'replace'</code> (which in this case
 
inserts a question mark instead of the unencodable character), there is
 
also <code>'xmlcharrefreplace'</code> (inserts an XML character reference),
 
<code>backslashreplace</code> (inserts a <code>\uNNNN</code> escape sequence) and
 
<code>namereplace</code> (inserts a <code>\N{...}</code> escape sequence).
 
  
The following example shows the different results:
+
以下示例显示了不同的结果:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第318行: 第230行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; u = chr(40960) + 'abcd' + chr(1972)
+
<syntaxhighlight lang="python3">>>> u = chr(40960) + 'abcd' + chr(1972)
&gt;&gt;&gt; u.encode('utf-8')
+
>>> u.encode('utf-8')
 
b'\xea\x80\x80abcd\xde\xb4'
 
b'\xea\x80\x80abcd\xde\xb4'
&gt;&gt;&gt; u.encode('ascii')   
+
>>> u.encode('ascii')   
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
 
UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
 
UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
 
   position 0: ordinal not in range(128)
 
   position 0: ordinal not in range(128)
&gt;&gt;&gt; u.encode('ascii', 'ignore')
+
>>> u.encode('ascii', 'ignore')
 
b'abcd'
 
b'abcd'
&gt;&gt;&gt; u.encode('ascii', 'replace')
+
>>> u.encode('ascii', 'replace')
 
b'?abcd?'
 
b'?abcd?'
&gt;&gt;&gt; u.encode('ascii', 'xmlcharrefreplace')
+
>>> u.encode('ascii', 'xmlcharrefreplace')
b'&amp;#40960;abcd&amp;#1972;'
+
b'&#40960;abcd&#1972;'
&gt;&gt;&gt; u.encode('ascii', 'backslashreplace')
+
>>> u.encode('ascii', 'backslashreplace')
 
b'\\ua000abcd\\u07b4'
 
b'\\ua000abcd\\u07b4'
&gt;&gt;&gt; u.encode('ascii', 'namereplace')
+
>>> u.encode('ascii', 'namereplace')
b'\\N{YI SYLLABLE IT}abcd\\u07b4'</pre>
+
b'\\N{YI SYLLABLE IT}abcd\\u07b4'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The low-level routines for registering and accessing the available
+
用于注册和访问可用编码的低级例程可在 [[../../library/codecs#module-codecs|codecs]] 模块中找到。 实现新编码还需要了解 [[../../library/codecs#module-codecs|codecs]] 模块。 然而,这个模块返回的编码和解码函数通常比较低级而不舒服,编写新的编码是一项专门的任务,因此本 HOWTO 不会涉及该模块。
encodings are found in the [[../../library/codecs#module-codecs|<code>codecs</code>]] module. Implementing new
 
encodings also requires understanding the [[../../library/codecs#module-codecs|<code>codecs</code>]] module.
 
However, the encoding and decoding functions returned by this module
 
are usually more low-level than is comfortable, and writing new encodings
 
is a specialized task, so the module won't be covered in this HOWTO.
 
  
  
第351行: 第258行:
 
<div id="unicode-literals-in-python-source-code" class="section">
 
<div id="unicode-literals-in-python-source-code" class="section">
  
=== Unicode Literals in Python Source Code ===
+
=== Python 源代码中的 Unicode 文字 ===
  
In Python source code, specific Unicode code points can be written using the
+
Python 源代码中,可以使用 <code>\u</code> 转义序列编写特定的 Unicode 代码点,后跟四个给出代码点的十六进制数字。 <code>\U</code> 转义序列类似,但需要八个十六进制数字,而不是四个:
<code>\u</code> escape sequence, which is followed by four hex digits giving the code
 
point. The <code>\U</code> escape sequence is similar, but expects eight hex digits,
 
not four:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第362行: 第266行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; s = &quot;a\xac\u1234\u20ac\U00008000&quot;
+
<syntaxhighlight lang="python3">>>> s = "a\xac\u1234\u20ac\U00008000"
 
... #    ^^^^ two-digit hex escape
 
... #    ^^^^ two-digit hex escape
 
... #        ^^^^^^ four-digit Unicode escape
 
... #        ^^^^^^ four-digit Unicode escape
 
... #                    ^^^^^^^^^^ eight-digit Unicode escape
 
... #                    ^^^^^^^^^^ eight-digit Unicode escape
&gt;&gt;&gt; [ord(c) for c in s]
+
>>> [ord(c) for c in s]
[97, 172, 4660, 8364, 32768]</pre>
+
[97, 172, 4660, 8364, 32768]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Using escape sequences for code points greater than 127 is fine in small doses,
+
对大于 127 的代码点使用转义序列在小剂量下是没问题的,但是如果您使用许多重音字符,就像在包含法语或其他一些使用重音的语言的消息的程序中一样,这会变得很烦人。 您还可以使用 [[../../library/functions#chr|chr()]] 内置函数组合字符串,但这更加乏味。
but becomes an annoyance if you're using many accented characters, as you would
 
in a program with messages in French or some other accent-using language. You
 
can also assemble strings using the [[../../library/functions#chr|<code>chr()</code>]] built-in function, but this is
 
even more tedious.
 
  
Ideally, you'd want to be able to write literals in your language's natural
+
理想情况下,您希望能够以您的语言的自然编码编写文字。 然后,您可以使用您最喜欢的编辑器编辑 Python 源代码,该编辑器会自然地显示重音字符,并在运行时使用正确的字符。
encoding. You could then edit Python source code with your favorite editor
 
which would display the accented characters naturally, and have the right
 
characters used at runtime.
 
  
Python supports writing source code in UTF-8 by default, but you can use almost
+
默认情况下,Python 支持以 UTF-8 编写源代码,但如果您声明正在使用的编码,则几乎可以使用任何编码。 这是通过在源文件的第一行或第二行包含一个特殊注释来完成的:
any encoding if you declare the encoding being used. This is done by including
 
a special comment as either the first or second line of the source file:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第391行: 第286行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#!/usr/bin/env python
+
<syntaxhighlight lang="python3">#!/usr/bin/env python
 
# -*- coding: latin-1 -*-
 
# -*- coding: latin-1 -*-
  
 
u = 'abcdé'
 
u = 'abcdé'
print(ord(u[-1]))</pre>
+
print(ord(u[-1]))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The syntax is inspired by Emacs's notation for specifying variables local to a
+
该语法的灵感来自 Emacs 用于指定文件局部变量的符号。 Emacs 支持许多不同的变量,但 Python 仅支持“编码”。 <code>-*-</code> 符号向 Emacs 表明该注释是特殊的; 它们对 Python 没有意义,而是一种约定。 Python 在注释中查找 <code>coding: name</code> <code>coding=name</code>
file. Emacs supports many different variables, but Python only supports
 
'coding'. The <code>-*-</code> symbols indicate to Emacs that the comment is special;
 
they have no significance to Python but are a convention. Python looks for
 
<code>coding: name</code> or <code>coding=name</code> in the comment.
 
  
If you don't include such a comment, the default encoding used will be UTF-8 as
+
如果您不包含这样的注释,则使用的默认编码将是前面提到的 UTF-8。 另见 <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0263 PEP 263] 了解更多信息。
already mentioned. See also <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0263 '''PEP 263'''] for more information.
 
  
  
第413行: 第303行:
 
<div id="unicode-properties" class="section">
 
<div id="unicode-properties" class="section">
  
=== Unicode Properties ===
+
=== Unicode 属性 ===
  
The Unicode specification includes a database of information about
+
Unicode 规范包括一个有关代码点的信息数据库。 对于每个定义的代码点,信息包括字符的名称、类别、数值(如果适用)(对于表示数字概念的字符,如罗马数字、分数如三分之一和五分之四等)。 还有显示相关的属性,比如如何在双向文本中使用代码点。
code points. For each defined code point, the information includes
 
the character's name, its category, the numeric value if applicable
 
(for characters representing numeric concepts such as the Roman
 
numerals, fractions such as one-third and four-fifths, etc.). There
 
are also display-related properties, such as how to use the code point
 
in bidirectional text.
 
  
The following program displays some information about several characters, and
+
下面的程序显示关于几个字符的一些信息,并打印一个特定字符的数值:
prints the numeric value of one particular character:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第430行: 第313行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import unicodedata
+
<syntaxhighlight lang="python3">import unicodedata
  
 
u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)
 
u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)
  
 
for i, c in enumerate(u):
 
for i, c in enumerate(u):
     print(i, '%04x' % ord(c), unicodedata.category(c), end=&quot; &quot;)
+
     print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")
 
     print(unicodedata.name(c))
 
     print(unicodedata.name(c))
  
 
# Get numeric value of second character
 
# Get numeric value of second character
print(unicodedata.numeric(u[1]))</pre>
+
print(unicodedata.numeric(u[1]))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When run, this prints:
+
运行时,这会打印:
  
 
<div class="highlight-none notranslate">
 
<div class="highlight-none notranslate">
第450行: 第333行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
+
<pre class="none">0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
 
1 0bf2 No TAMIL NUMBER ONE THOUSAND
 
1 0bf2 No TAMIL NUMBER ONE THOUSAND
 
2 0f84 Mn TIBETAN MARK HALANTA
 
2 0f84 Mn TIBETAN MARK HALANTA
第460行: 第343行:
  
 
</div>
 
</div>
The category codes are abbreviations describing the nature of the character.
+
类别代码是描述字符性质的缩写。 这些被分组为“字母”、“数字”、“标点符号”或“符号”等类别,而这些类别又分为子类别。 从上面的输出中获取代码,<code>'Ll'</code> 表示“字母,小写”,<code>'No'</code> 表示“数字,其他”,<code>'Mn'</code> 表示“标记,非空格”,[ X148X] 是“符号,其他”。 有关类别代码列表,请参阅 Unicode 字符数据库文档 [https://www.unicode.org/reports/tr44/#General_Category_Values ] 通用类别值部分。
These are grouped into categories such as &quot;Letter&quot;, &quot;Number&quot;, &quot;Punctuation&quot;, or
 
&quot;Symbol&quot;, which in turn are broken up into subcategories. To take the codes
 
from the above output, <code>'Ll'</code> means 'Letter, lowercase', <code>'No'</code> means
 
&quot;Number, other&quot;, <code>'Mn'</code> is &quot;Mark, nonspacing&quot;, and <code>'So'</code> is &quot;Symbol,
 
other&quot;. See
 
[https://www.unicode.org/reports/tr44/#General_Category_Values the General Category Values section of the Unicode Character Database documentation] for a
 
list of category codes.
 
  
  
第473行: 第349行:
 
<div id="comparing-strings" class="section">
 
<div id="comparing-strings" class="section">
  
=== Comparing Strings ===
+
=== 比较字符串 ===
  
Unicode adds some complication to comparing strings, because the same
+
Unicode 为比较字符串增加了一些复杂性,因为同一组字符可以由不同的代码点序列表示。 例如,像“ê”这样的字母可以表示为单个代码点 U+00EA,或表示为 U+0065 U+0302,即“e”的代码点后跟“COMBINING CIRCUMFLEX ACCENT”的代码点. 这些将在打印时产生相同的输出,但一个是长度为 1 的字符串,另一个是长度为 2 的字符串。
set of characters can be represented by different sequences of code
 
points. For example, a letter like 'ê' can be represented as a single
 
code point U+00EA, or as U+0065 U+0302, which is the code point for
 
'e' followed by a code point for 'COMBINING CIRCUMFLEX ACCENT'. These
 
will produce the same output when printed, but one is a string of
 
length 1 and the other is of length 2.
 
  
One tool for a case-insensitive comparison is the
+
一种不区分大小写比较的工具是 [[../../library/stdtypes#str|casefold()]] 字符串方法,该方法根据 Unicode 标准描述的算法将字符串转换为不区分大小写的形式。 该算法对诸如德语字母“ß”(代码点 U+00DF)之类的字符进行了特殊处理,它变成了一对小写字母“ss”。
[[../../library/stdtypes#str|<code>casefold()</code>]] string method that converts a string to a
 
case-insensitive form following an algorithm described by the Unicode
 
Standard. This algorithm has special handling for characters such as
 
the German letter 'ß' (code point U+00DF), which becomes the pair of
 
lowercase letters 'ss'.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第494行: 第359行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; street = 'Gürzenichstraße'
+
<syntaxhighlight lang="python3">>>> street = 'Gürzenichstraße'
&gt;&gt;&gt; street.casefold()
+
>>> street.casefold()
'gürzenichstrasse'</pre>
+
'gürzenichstrasse'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
A second tool is the [[../../library/unicodedata#module-unicodedata|<code>unicodedata</code>]] module's
+
第二个工具是 [[../../library/unicodedata#module-unicodedata|unicodedata]] 模块的 [[../../library/unicodedata#unicodedata|normalize()]] 函数,该函数将字符串转换为几种正常形式之一,其中字母后跟组合字符被替换为单个字符。 <code>normalize()</code> 可用于执行字符串比较,如果两个字符串使用不同的组合字符,则不会错误地报告不等式:
[[../../library/unicodedata#unicodedata|<code>normalize()</code>]] function that converts strings to one
 
of several normal forms, where letters followed by a combining
 
character are replaced with single characters. <code>normalize()</code> can
 
be used to perform string comparisons that won't falsely report
 
inequality if two strings use combining characters differently:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第512行: 第372行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import unicodedata
+
<syntaxhighlight lang="python3">import unicodedata
  
 
def compare_strs(s1, s2):
 
def compare_strs(s1, s2):
第524行: 第384行:
 
print('length of first string=', len(single_char))
 
print('length of first string=', len(single_char))
 
print('length of second string=', len(multiple_chars))
 
print('length of second string=', len(multiple_chars))
print(compare_strs(single_char, multiple_chars))</pre>
+
print(compare_strs(single_char, multiple_chars))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When run, this outputs:
+
运行时,输出:
  
 
<div class="highlight-shell-session notranslate">
 
<div class="highlight-shell-session notranslate">
第535行: 第395行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>$ python3 compare-strs.py
+
<pre class="session">$ python3 compare-strs.py
 
length of first string= 1
 
length of first string= 1
 
length of second string= 2
 
length of second string= 2
第543行: 第403行:
  
 
</div>
 
</div>
The first argument to the [[../../library/unicodedata#unicodedata|<code>normalize()</code>]] function is a
+
[[../../library/unicodedata#unicodedata|normalize()]] 函数的第一个参数是一个字符串,给出所需的规范化形式,可以是“NFC”、“NFKC”、“NFD”和“NFKD”之一。
string giving the desired normalization form, which can be one of
 
'NFC', 'NFKC', 'NFD', and 'NFKD'.
 
  
The Unicode Standard also specifies how to do caseless comparisons:
+
Unicode 标准还指定了如何进行无大小写比较:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第553行: 第411行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import unicodedata
+
<syntaxhighlight lang="python3">import unicodedata
  
 
def compare_caseless(s1, s2):
 
def compare_caseless(s1, s2):
第565行: 第423行:
 
multiple_chars = '\N{LATIN CAPITAL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
 
multiple_chars = '\N{LATIN CAPITAL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
  
print(compare_caseless(single_char, multiple_chars))</pre>
+
print(compare_caseless(single_char, multiple_chars))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This will print <code>True</code>. (Why is <code>NFD()</code> invoked twice? Because
+
这将打印 <code>True</code>。 (为什么 <code>NFD()</code> 被调用了两次? 因为有几个字符使 <code>casefold()</code> 返回一个非规范化的字符串,所以结果需要再次规范化。 有关讨论和示例,请参阅 Unicode 标准的第 3.13 节。)
there are a few characters that make <code>casefold()</code> return a
 
non-normalized string, so the result needs to be normalized again. See
 
section 3.13 of the Unicode Standard for a discussion and an example.)
 
  
  
第579行: 第434行:
 
<div id="unicode-regular-expressions" class="section">
 
<div id="unicode-regular-expressions" class="section">
  
=== Unicode Regular Expressions ===
+
=== Unicode 正则表达式 ===
  
The regular expressions supported by the [[../../library/re#module-re|<code>re</code>]] module can be provided
+
[[../../library/re#module-re|re]] 模块支持的正则表达式可以以字节或字符串形式提供。 某些特殊字符序列(例如 <code>\d</code> <code>\w</code>)具有不同的含义,具体取决于模式是以字节还是字符串形式提供的。 例如,<code>\d</code> 将匹配字节中的字符 <code>[0-9]</code>,但在字符串中将匹配 <code>'Nd'</code> 类别中的任何字符。
either as bytes or strings. Some of the special character sequences such as
 
<code>\d</code> and <code>\w</code> have different meanings depending on whether
 
the pattern is supplied as bytes or a string. For example,
 
<code>\d</code> will match the characters <code>[0-9]</code> in bytes but
 
in strings will match any character that's in the <code>'Nd'</code> category.
 
  
The string in this example has the number 57 written in both Thai and
+
此示例中的字符串具有用泰语和阿拉伯数字书写的数字 57:
Arabic numerals:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第595行: 第444行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import re
+
<syntaxhighlight lang="python3">import re
 
p = re.compile(r'\d+')
 
p = re.compile(r'\d+')
  
s = &quot;Over \u0e55\u0e57 57 flavours&quot;
+
s = "Over \u0e55\u0e57 57 flavours"
 
m = p.search(s)
 
m = p.search(s)
print(repr(m.group()))</pre>
+
print(repr(m.group()))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When executed, <code>\d+</code> will match the Thai numerals and print them
+
执行时,<code>\d+</code> 会匹配泰语数字并打印出来。 如果您将 [[../../library/re#re|re.ASCII]] 标志提供给 [[../../library/re#re|compile()]],则 <code>\d+</code> 将匹配子字符串“57”。
out. If you supply the [[../../library/re#re|<code>re.ASCII</code>]] flag to
 
[[../../library/re#re|<code>compile()</code>]], <code>\d+</code> will match the substring &quot;57&quot; instead.
 
  
Similarly, <code>\w</code> matches a wide variety of Unicode characters but
+
类似地,<code>\w</code> 匹配多种 Unicode 字符,但仅 <code>[a-zA-Z0-9_]</code> 以字节为单位,或者如果提供 [[../../library/re#re|re.ASCII]],并且 <code>\s</code> 将匹配任一 Unicode空白字符或 <code>[ \t\n\r\f\v]</code>
only <code>[a-zA-Z0-9_]</code> in bytes or if [[../../library/re#re|<code>re.ASCII</code>]] is supplied,
 
and <code>\s</code> will match either Unicode whitespace characters or
 
<code>[ \t\n\r\f\v]</code>.
 
  
  
第618行: 第462行:
 
<div id="id2" class="section">
 
<div id="id2" class="section">
  
=== References ===
+
=== 参考 ===
  
Some good alternative discussions of Python's Unicode support are:
+
关于 Python Unicode 支持的一些很好的替代讨论是:
  
* [http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html Processing Text Files in Python 3], by Nick Coghlan.
+
* [http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html Python 3 中处理文本文件] ,作者 Nick Coghlan。
* [https://nedbatchelder.com/text/unipain.html Pragmatic Unicode], a PyCon 2012 presentation by Ned Batchelder.
+
* [https://nedbatchelder.com/text/unipain.html Pragmatic Unicode],Ned Batchelder 在 PyCon 2012 上的演讲。
  
The [[../../library/stdtypes#str|<code>str</code>]] type is described in the Python library reference at
+
[[../../library/stdtypes#str|str]] 类型在 Python 库参考 [[../../library/stdtypes#textseq|文本序列类型 — str]] 中进行了描述。
[[../../library/stdtypes#textseq|<span class="std std-ref">Text Sequence Type --- str</span>]].
 
  
The documentation for the [[../../library/unicodedata#module-unicodedata|<code>unicodedata</code>]] module.
+
[[../../library/unicodedata#module-unicodedata|unicodedata]] 模块的文档。
  
The documentation for the [[../../library/codecs#module-codecs|<code>codecs</code>]] module.
+
[[../../library/codecs#module-codecs|codecs]] 模块的文档。
  
Marc-André Lemburg gave [https://downloads.egenix.com/python/Unicode-EPC2002-Talk.pdf a presentation titled &quot;Python and Unicode&quot; (PDF slides)] at
+
Marc-André Lemburg 在 EuroPython 2002 上做了一个题为“Python 和 Unicode”(PDF 幻灯片) [https://downloads.egenix.com/python/Unicode-EPC2002-Talk.pdf 的演讲] 。 这些幻灯片很好地概述了 Python 2 Unicode 功能的设计(其中 Unicode 字符串类型称为 <code>unicode</code>,文字以 <code>u</code> 开头)。
EuroPython 2002. The slides are an excellent overview of the design of Python
 
2's Unicode features (where the Unicode string type is called <code>unicode</code> and
 
literals start with <code>u</code>).
 
  
  
第643行: 第483行:
 
<div id="reading-and-writing-unicode-data" class="section">
 
<div id="reading-and-writing-unicode-data" class="section">
  
== Reading and Writing Unicode Data ==
+
== 读取和写入 Unicode 数据 ==
  
Once you've written some code that works with Unicode data, the next problem is
+
一旦您编写了一些处理 Unicode 数据的代码,下一个问题就是输入/输出。 你如何将Unicode字符串放入你的程序中,你如何将Unicode转换成适合存储或传输的形式?
input/output. How do you get Unicode strings into your program, and how do you
 
convert Unicode into a form suitable for storage or transmission?
 
  
It's possible that you may not need to do anything depending on your input
+
根据您的输入源和输出目的地,您可能不需要做任何事情; 您应该检查应用程序中使用的库是否原生支持 Unicode。 例如,XML 解析器经常返回 Unicode 数据。 许多关系数据库还支持 Unicode 值列,并且可以从 SQL 查询返回 Unicode 值。
sources and output destinations; you should check whether the libraries used in
 
your application support Unicode natively. XML parsers often return Unicode
 
data, for example. Many relational databases also support Unicode-valued
 
columns and can return Unicode values from an SQL query.
 
  
Unicode data is usually converted to a particular encoding before it gets
+
Unicode 数据在写入磁盘或通过套接字发送之前通常会转换为特定的编码。 可以自己完成所有工作:打开一个文件,从中读取一个 8 位字节对象,然后使用 <code>bytes.decode(encoding)</code> 转换字节。 但是,不推荐手动方法。
written to disk or sent over a socket. It's possible to do all the work
 
yourself: open a file, read an 8-bit bytes object from it, and convert the bytes
 
with <code>bytes.decode(encoding)</code>. However, the manual approach is not recommended.
 
  
One problem is the multi-byte nature of encodings; one Unicode character can be
+
一个问题是编码的多字节性质; 一个 Unicode 字符可以用几个字节表示。 如果要以任意大小的块(例如 1024 4096 字节)读取文件,则需要编写错误处理代码来捕获在末尾仅读取编码单个 Unicode 字符的部分字节的情况一块。 一种解决方案是将整个文件读入内存,然后执行解码,但这会阻止您处理非常大的文件; 如果您需要读取 2 GiB 的文件,则需要 2 GiB 的 RAM。 (更多,真的,因为至少有一段时间你需要在内存中同时拥有编码字符串和它的 Unicode 版本。)
represented by several bytes. If you want to read the file in arbitrary-sized
 
chunks (say, 1024 or 4096 bytes), you need to write error-handling code to catch the case
 
where only part of the bytes encoding a single Unicode character are read at the
 
end of a chunk. One solution would be to read the entire file into memory and
 
then perform the decoding, but that prevents you from working with files that
 
are extremely large; if you need to read a 2 GiB file, you need 2 GiB of RAM.
 
(More, really, since for at least a moment you'd need to have both the encoded
 
string and its Unicode version in memory.)
 
  
The solution would be to use the low-level decoding interface to catch the case
+
解决方案是使用低级解码接口来捕获部分编码序列的情况。 实现这一点的工作已经为您完成:内置的 [[../../library/functions#open|open()]] 函数可以返回一个类文件对象,该对象假定文件的内容采用指定的编码,并接受 Unicode 参数用于诸如如 [[../../library/io#io.TextIOBase|read()]] [[../../library/io#io.TextIOBase|write()]]。 这通过 [[../../library/functions#open|open()]]'s ''encoding'' ''errors'' 参数起作用,这些参数的解释就像 [[../../library/stdtypes#str|str.encode()]][[../../library/stdtypes#bytes|bytes.decode()]]
of partial coding sequences. The work of implementing this has already been
 
done for you: the built-in [[../../library/functions#open|<code>open()</code>]] function can return a file-like object
 
that assumes the file's contents are in a specified encoding and accepts Unicode
 
parameters for methods such as [[../../library/io#io.TextIOBase|<code>read()</code>]] and
 
[[../../library/io#io.TextIOBase|<code>write()</code>]]. This works through [[../../library/functions#open|<code>open()</code>]]'s ''encoding'' and
 
''errors'' parameters which are interpreted just like those in [[../../library/stdtypes#str|<code>str.encode()</code>]]
 
and [[../../library/stdtypes#bytes|<code>bytes.decode()</code>]].
 
  
Reading Unicode from a file is therefore simple:
+
因此,从文件中读取 Unicode 很简单:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第685行: 第501行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with open('unicode.txt', encoding='utf-8') as f:
+
<syntaxhighlight lang="python3">with open('unicode.txt', encoding='utf-8') as f:
 
     for line in f:
 
     for line in f:
         print(repr(line))</pre>
+
         print(repr(line))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
It's also possible to open files in update mode, allowing both reading and
+
也可以在更新模式下打开文件,允许读取和写入:
writing:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第699行: 第514行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with open('test', encoding='utf-8', mode='w+') as f:
+
<syntaxhighlight lang="python3">with open('test', encoding='utf-8', mode='w+') as f:
 
     f.write('\u4500 blah blah blah\n')
 
     f.write('\u4500 blah blah blah\n')
 
     f.seek(0)
 
     f.seek(0)
     print(repr(f.readline()[:1]))</pre>
+
     print(repr(f.readline()[:1]))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The Unicode character <code>U+FEFF</code> is used as a byte-order mark (BOM), and is often
+
Unicode 字符 <code>U+FEFF</code> 用作字节顺序标记 (BOM),通常作为文件的第一个字符写入,以帮助自动检测文件的字节顺序。 某些编码(例如 UTF-16)期望在文件开头出现 BOM; 当使用这种编码时,BOM 将自动作为第一个字符写入,并在读取文件时静默删除。 这些编码有多种变体,例如用于 little-endian 和 big-endian 编码的 'utf-16-le' 'utf-16-be',它们指定一种特定的字节顺序并且不跳过 BOM。
written as the first character of a file in order to assist with autodetection
 
of the file's byte ordering. Some encodings, such as UTF-16, expect a BOM to be
 
present at the start of a file; when such an encoding is used, the BOM will be
 
automatically written as the first character and will be silently dropped when
 
the file is read. There are variants of these encodings, such as 'utf-16-le'
 
and 'utf-16-be' for little-endian and big-endian encodings, that specify one
 
particular byte ordering and don't skip the BOM.
 
  
In some areas, it is also convention to use a &quot;BOM&quot; at the start of UTF-8
+
在某些地区,在 UTF-8 编码文件的开头使用“BOM”也是惯例; 该名称具有误导性,因为 UTF-8 不依赖于字节顺序。 该标记只是声明文件以 UTF-8 编码。 要读取此类文件,请使用“utf-8-sig”编解码器自动跳过标记(如果存在)。
encoded files; the name is misleading since UTF-8 is not byte-order dependent.
 
The mark simply announces that the file is encoded in UTF-8. For reading such
 
files, use the 'utf-8-sig' codec to automatically skip the mark if present.
 
  
 
<div id="unicode-filenames" class="section">
 
<div id="unicode-filenames" class="section">
  
=== Unicode filenames ===
+
=== Unicode 文件名 ===
  
Most of the operating systems in common use today support filenames
+
当今常用的大多数操作系统都支持包含任意 Unicode 字符的文件名。 通常这是通过将 Unicode 字符串转换为某种因系统而异的编码来实现的。 今天,Python 趋向于使用 UTF-8:MacOS 上的 Python 已经在多个版本中使用了 UTF-8,而 Python 3.6 在 Windows 上也改用了 UTF-8。 在 Unix 系统上,只有设置了 <code>LANG</code> <code>LC_CTYPE</code> 环境变量,才会有文件系统编码; 如果还没有,默认编码又是 UTF-8。
that contain arbitrary Unicode characters. Usually this is
 
implemented by converting the Unicode string into some encoding that
 
varies depending on the system. Today Python is converging on using
 
UTF-8: Python on MacOS has used UTF-8 for several versions, and Python
 
3.6 switched to using UTF-8 on Windows as well. On Unix systems,
 
there will only be a filesystem encoding if you've set the <code>LANG</code> or
 
<code>LC_CTYPE</code> environment variables; if you haven't, the default
 
encoding is again UTF-8.
 
  
The [[../../library/sys#sys|<code>sys.getfilesystemencoding()</code>]] function returns the encoding to use on
+
[[../../library/sys#sys|sys.getfilesystemencoding()]] 函数返回要在当前系统上使用的编码,以防您想手动进行编码,但没有太多理由费心。 打开文件进行读取或写入时,通常只需提供 Unicode 字符串作为文件名,它就会自动转换为正确的编码:
your current system, in case you want to do the encoding manually, but there's
 
not much reason to bother. When opening a file for reading or writing, you can
 
usually just provide the Unicode string as the filename, and it will be
 
automatically converted to the right encoding for you:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第745行: 第538行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>filename = 'filename\u4500abc'
+
<syntaxhighlight lang="python3">filename = 'filename\u4500abc'
 
with open(filename, 'w') as f:
 
with open(filename, 'w') as f:
     f.write('blah\n')</pre>
+
     f.write('blah\n')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Functions in the [[../../library/os#module-os|<code>os</code>]] module such as [[../../library/os#os|<code>os.stat()</code>]] will also accept Unicode
+
[[../../library/os#module-os|os]] 模块中的函数,例如 [[../../library/os#os|os.stat()]] 也将接受 Unicode 文件名。
filenames.
 
  
The [[../../library/os#os|<code>os.listdir()</code>]] function returns filenames, which raises an issue: should it return
+
[[../../library/os#os|os.listdir()]] 函数返回文件名,这引发了一个问题:它应该返回文件名的 Unicode 版本,还是应该返回包含编码版本的字节? [[../../library/os#os|os.listdir()]] 两者都可以,具体取决于您提供的目录路径是字节还是 Unicode 字符串。 如果您将 Unicode 字符串作为路径传递,则文件名将使用文件系统的编码进行解码并返回 Unicode 字符串列表,而传递字节路径将以字节形式返回文件名。 例如,假设默认文件系统编码为 UTF-8,运行以下程序:
the Unicode version of filenames, or should it return bytes containing
 
the encoded versions? [[../../library/os#os|<code>os.listdir()</code>]] can do both, depending on whether you
 
provided the directory path as bytes or a Unicode string. If you pass a
 
Unicode string as the path, filenames will be decoded using the filesystem's
 
encoding and a list of Unicode strings will be returned, while passing a byte
 
path will return the filenames as bytes. For example,
 
assuming the default filesystem encoding is UTF-8, running the following
 
program:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第769行: 第553行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>fn = 'filename\u4500abc'
+
<syntaxhighlight lang="python3">fn = 'filename\u4500abc'
 
f = open(fn, 'w')
 
f = open(fn, 'w')
 
f.close()
 
f.close()
第775行: 第559行:
 
import os
 
import os
 
print(os.listdir(b'.'))
 
print(os.listdir(b'.'))
print(os.listdir('.'))</pre>
+
print(os.listdir('.'))</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
will produce the following output:
+
将产生以下输出:
  
 
<div class="highlight-shell-session notranslate">
 
<div class="highlight-shell-session notranslate">
第786行: 第570行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>$ python listdir-test.py
+
<pre class="session">$ python listdir-test.py
 
[b'filename\xe4\x94\x80abc', ...]
 
[b'filename\xe4\x94\x80abc', ...]
 
['filename\u4500abc', ...]</pre>
 
['filename\u4500abc', ...]</pre>
第793行: 第577行:
  
 
</div>
 
</div>
The first list contains UTF-8-encoded filenames, and the second list contains
+
第一个列表包含 UTF-8 编码的文件名,第二个列表包含 Unicode 版本。
the Unicode versions.
 
  
Note that on most occasions, you should can just stick with using
+
请注意,在大多数情况下,您应该坚持使用带有这些 API 的 Unicode。 字节 API 应仅用于可能存在不可解码文件名的系统; 现在几乎只有 Unix 系统。
Unicode with these APIs. The bytes APIs should only be used on
 
systems where undecodable file names can be present; that's
 
pretty much only Unix systems now.
 
  
  
第805行: 第585行:
 
<div id="tips-for-writing-unicode-aware-programs" class="section">
 
<div id="tips-for-writing-unicode-aware-programs" class="section">
  
=== Tips for Writing Unicode-aware Programs ===
+
=== 编写可识别 Unicode 的程序的技巧 ===
  
This section provides some suggestions on writing software that deals with
+
本节提供了一些关于编写处理 Unicode 的软件的建议。
Unicode.
 
  
The most important tip is:
+
最重要的提示是:
  
 
<blockquote><div>
 
<blockquote><div>
  
Software should only work with Unicode strings internally, decoding the input
+
软件应该只在内部处理 Unicode 字符串,尽快解码输入数据并只在最后对输出进行编码。
data as soon as possible and encoding the output only at the end.
 
  
  
 
</div></blockquote>
 
</div></blockquote>
If you attempt to write processing functions that accept both Unicode and byte
+
如果您尝试编写同时接受 Unicode 和字节字符串的处理函数,您会发现您的程序在组合这两种不同类型的字符串的任何地方都容易受到错误的影响。 没有自动编码或解码:如果你这样做 <code>str + bytes</code>,一个 [[../../library/exceptions#TypeError|TypeError]] 将被引发。
strings, you will find your program vulnerable to bugs wherever you combine the
 
two different kinds of strings. There is no automatic encoding or decoding: if
 
you do e.g. <code>str + bytes</code>, a [[../../library/exceptions#TypeError|<code>TypeError</code>]] will be raised.
 
  
When using data coming from a web browser or some other untrusted source, a
+
当使用来自 Web 浏览器或其他不受信任来源的数据时,常用的技术是在生成的命令行中使用字符串或将其存储在数据库中之前检查字符串中的非法字符。 如果您这样做,请小心检查解码的字符串,而不是编码的字节数据; 一些编码可能具有有趣的特性,例如不是双射的或不完全兼容 ASCII。 如果输入数据还指定了编码,则尤其如此,因为攻击者可以选择一种巧妙的方式将恶意文本隐藏在编码的字节流中。
common technique is to check for illegal characters in a string before using the
 
string in a generated command line or storing it in a database. If you're doing
 
this, be careful to check the decoded string, not the encoded bytes data;
 
some encodings may have interesting properties, such as not being bijective
 
or not being fully ASCII-compatible. This is especially true if the input
 
data also specifies the encoding, since the attacker can then choose a
 
clever way to hide malicious text in the encoded bytestream.
 
  
 
<div id="converting-between-file-encodings" class="section">
 
<div id="converting-between-file-encodings" class="section">
  
==== Converting Between File Encodings ====
+
==== 在文件编码之间转换 ====
  
The [[../../library/codecs#codecs|<code>StreamRecoder</code>]] class can transparently convert between
+
[[../../library/codecs#codecs|StreamRecoder]] 类可以在编码之间透明地转换,获取以编码 #1 返回数据的流,并且表现得像以编码 #2 返回数据的流。
encodings, taking a stream that returns data in encoding #1
 
and behaving like a stream returning data in encoding #2.
 
  
For example, if you have an input file ''f'' that's in Latin-1, you
+
例如,如果您有一个使用 Latin-1 格式的输入文件 ''f'',您可以用 [[../../library/codecs#codecs|StreamRecoder]] 包装它以返回以 UTF-8 编码的字节:
can wrap it with a [[../../library/codecs#codecs|<code>StreamRecoder</code>]] to return bytes encoded in
 
UTF-8:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第849行: 第613行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>new_f = codecs.StreamRecoder(f,
+
<syntaxhighlight lang="python3">new_f = codecs.StreamRecoder(f,
 
     # en/decoder: used by read() to encode its results and
 
     # en/decoder: used by read() to encode its results and
 
     # by write() to decode its input.
 
     # by write() to decode its input.
第855行: 第619行:
  
 
     # reader/writer: used to read and write to the stream.
 
     # reader/writer: used to read and write to the stream.
     codecs.getreader('latin-1'), codecs.getwriter('latin-1') )</pre>
+
     codecs.getreader('latin-1'), codecs.getwriter('latin-1') )</syntaxhighlight>
  
 
</div>
 
</div>
第864行: 第628行:
 
<div id="files-in-an-unknown-encoding" class="section">
 
<div id="files-in-an-unknown-encoding" class="section">
  
==== Files in an Unknown Encoding ====
+
==== 未知编码的文件 ====
  
What can you do if you need to make a change to a file, but don't know
+
如果您需要对文件进行更改,但不知道文件的编码,该怎么办? 如果您知道编码与 ASCII 兼容并且只想检查或修改 ASCII 部分,您可以使用 <code>surrogateescape</code> 错误处理程序打开文件:
the file's encoding? If you know the encoding is ASCII-compatible and
 
only want to examine or modify the ASCII parts, you can open the file
 
with the <code>surrogateescape</code> error handler:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第875行: 第636行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with open(fname, 'r', encoding=&quot;ascii&quot;, errors=&quot;surrogateescape&quot;) as f:
+
<syntaxhighlight lang="python3">with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
 
     data = f.read()
 
     data = f.read()
  
第881行: 第642行:
  
 
with open(fname + '.new', 'w',
 
with open(fname + '.new', 'w',
           encoding=&quot;ascii&quot;, errors=&quot;surrogateescape&quot;) as f:
+
           encoding="ascii", errors="surrogateescape") as f:
     f.write(data)</pre>
+
     f.write(data)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The <code>surrogateescape</code> error handler will decode any non-ASCII bytes
+
<code>surrogateescape</code> 错误处理程序会将任何非 ASCII 字节解码为从 U+DC80 U+DCFF 的特殊范围内的代码点。 当 <code>surrogateescape</code> 错误处理程序用于对数据进行编码并将其写回时,这些代码点将转回相同的字节。
as code points in a special range running from U+DC80 to
 
U+DCFF. These code points will then turn back into the
 
same bytes when the <code>surrogateescape</code> error handler is used to
 
encode the data and write it back out.
 
  
  
第899行: 第656行:
 
<div id="id3" class="section">
 
<div id="id3" class="section">
  
=== References ===
+
=== 参考 ===
  
One section of [http://pyvideo.org/video/289/pycon-2010--mastering-python-3-i-o Mastering Python 3 Input/Output],
+
[http://pyvideo.org/video/289/pycon-2010--mastering-python-3-i-o Mastering Python 3 Input/Output] 的一部分,David Beazley 在 PyCon 2010 上的演讲,讨论了文本处理和二进制数据处理。
a PyCon 2010 talk by David Beazley, discusses text processing and binary data handling.
 
  
The [https://downloads.egenix.com/python/LSM2005-Developing-Unicode-aware-applications-in-Python.pdf PDF slides for Marc-André Lemburg's presentation &quot;Writing Unicode-aware
+
Marc-André Lemburg 的演讲“Writing Unicode-aware Applications in Python” [https://downloads.egenix.com/python/LSM2005-Developing-Unicode-aware-applications-in-Python.pdf 的] PDF 幻灯片讨论了字符编码问题以及如何国际化和本地化应用程序。 这些幻灯片仅涵盖 Python 2.x。
Applications in Python&quot;]
 
discuss questions of character encodings as well as how to internationalize
 
and localize an application. These slides cover Python 2.x only.
 
  
[http://pyvideo.org/video/1768/the-guts-of-unicode-in-python The Guts of Unicode in Python]
+
[http://pyvideo.org/video/1768/the-guts-of-unicode-in-python The Guts of Unicode in Python] 是 Benjamin Peterson 在 PyCon 2013 上的演讲,讨论了 Python 3.3 中的内部 Unicode 表示。
is a PyCon 2013 talk by Benjamin Peterson that discusses the internal Unicode
 
representation in Python 3.3.
 
  
  
第919行: 第670行:
 
<div id="acknowledgements" class="section">
 
<div id="acknowledgements" class="section">
  
== Acknowledgements ==
+
== 致谢 ==
  
The initial draft of this document was written by Andrew Kuchling.
+
本文档的初稿由 Andrew Kuchling 撰写。 此后,Alexander Belopolsky、Georg Brandl、Andrew Kuchling Ezio Melotti 对其进行了进一步修订。
It has since been revised further by Alexander Belopolsky, Georg Brandl,
 
Andrew Kuchling, and Ezio Melotti.
 
  
Thanks to the following people who have noted errors or offered
+
感谢以下指出本文错误或提出建议的人:Éric Araujo、Nicholas Bastin、Nick Coghlan、Marius Gedminas、Kent Johnson、Ken Krugler、Marc-André Lemburg、Martin von Löwis、Terry J. Reedy、Serhiy Storchaka、Eryk Sun、Chad Whitacre、Graham Wideman。
suggestions on this article: Éric Araujo, Nicholas Bastin, Nick
 
Coghlan, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André
 
Lemburg, Martin von Löwis, Terry J. Reedy, Serhiy Storchaka,
 
Eryk Sun, Chad Whitacre, Graham Wideman.
 
  
  
第935行: 第680行:
  
 
</div>
 
</div>
 +
<div class="clearer">
  
[[Category:Python 3.9 中文文档]]
+
 
 +
 
 +
</div>
 +
 
 +
[[Category:Python 3.9 文档]]

2021年10月31日 (日) 04:51的最新版本

Unicode HOWTO

发布
1.12

本 HOWTO 讨论了 Python 对用于表示文本数据的 Unicode 规范的支持,并解释了人们在尝试使用 Unicode 时经常遇到的各种问题。

Unicode 简介

定义

今天的程序需要能够处理各种各样的字符。 应用程序通常是国际化的,以各种用户可选择的语言显示消息和输出; 同一程序可能需要以英语、法语、日语、希伯来语或俄语输出错误消息。 Web 内容可以用这些语言中的任何一种编写,还可以包含各种表情符号。 Python 的字符串类型使用 Unicode 标准来表示字符,这让 Python 程序可以处理所有这些不同的可能字符。

Unicode (https://www.unicode.org/) 是一种规范,旨在列出人类语言使用的每个字符,并为每个字符提供自己的唯一代码。 Unicode 规范不断修订和更新,以添加新的语言和符号。

character 是文本的最小可能组成部分。 'A'、'B'、'C' 等都是不同的字符。 'È' 和 'Í' 也是如此。 字符因您所谈论的语言或上下文而异。 例如,“罗马数字一”有一个字符“Ⅰ”,它与大写字母“I”分开。 它们通常看起来相同,但这是两个具有不同含义的不同字符。

Unicode 标准描述了字符如何由 码位 表示。 代码点值是 0 到 0x10FFFF 范围内的整数(大约 110 万个值, 分配的实际数字 小于该值)。 在标准和本文档中,代码点使用符号 U+265E 表示值为 0x265e(十进制为 9,822)的字符。

Unicode 标准包含许多列出字符及其相应代码点的表格:

0061    'a'; LATIN SMALL LETTER A
0062    'b'; LATIN SMALL LETTER B
0063    'c'; LATIN SMALL LETTER C
...
007B    '{'; LEFT CURLY BRACKET
...
2167    'Ⅷ'; ROMAN NUMERAL EIGHT
2168    'Ⅸ'; ROMAN NUMERAL NINE
...
265E    '♞'; BLACK CHESS KNIGHT
265F    '♟'; BLACK CHESS PAWN
...
1F600   '😀'; GRINNING FACE
1F609   '😉'; WINKING FACE
...

严格来说,这些定义意味着说“这是字符 U+265E”是没有意义的。 U+265E 是一个码位,代表某个特定的字符; 在本例中,它代表字符“BLACK CHESS KNIGHT”、“♞”。 在非正式环境中,有时会忘记代码点和字符之间的这种区别。

字符在屏幕或纸张上由一组称为 字形 的图形元素表示。 例如,大写字母 A 的字形是两个对角线笔画和一个水平笔画,但具体细节取决于所使用的字体。 大多数 Python 代码不需要担心字形; 找出要显示的正确字形通常是 GUI 工具包或终端的字体渲染器的工作。


编码

总结上一节:Unicode 字符串是一系列代码点,它们是从 0 到 0x10FFFF(十进制 1,114,111)的数字。 这个代码点序列需要在内存中表示为一组代码单元,然后将代码单元映射到8位字节。 将 Unicode 字符串转换为字节序列的规则称为 字符编码 ,或简称为 编码

您可能想到的第一种编码是使用 32 位整数作为代码单元,然后使用 CPU 对 32 位整数的表示。 在此表示中,字符串“Python”可能如下所示:

   P           y           t           h           o           n
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

这种表示很简单,但使用它会带来许多问题。

  1. 它不便携; 不同的处理器以不同的方式对字节进行排序。
  2. 非常浪费空间。 在大多数文本中,大多数代码点小于 127,或者小于 255,因此 0x00 字节占用了大量空间。 与 ASCII 表示所需的 6 个字节相比,上述字符串需要 24 个字节。 RAM 使用量的增加并不重要(台式计算机有 GB 的 RAM,而字符串通常不会那么大),但是将我们对磁盘和网络带宽的使用量增加 4 倍是无法忍受的。
  3. 它与现有的 C 函数(如 strlen())不兼容,因此需要使用新的宽字符串函数系列。

因此这种编码使用的不多,人们转而选择其他更高效、更方便的编码,例如UTF-8。

UTF-8 是最常用的编码之一,Python 通常默认使用它。 UTF 代表“Unicode 转换格式”,'8' 表示在编码中使用 8 位值。 (也有 UTF-16 和 UTF-32 编码,但它们的使用频率不如 UTF-8。)UTF-8 使用以下规则:

  1. 如果代码点 < 128,则由相应的字节值表示。
  2. 如果代码点 >= 128,则将其转换为两个、三个或四个字节的序列,其中该序列的每个字节介于 128 和 255 之间。

UTF-8 有几个方便的特性:

  1. 它可以处理任何 Unicode 代码点。
  2. Unicode 字符串被转换为一个字节序列,其中仅在它们表示空字符 (U+0000) 的地方包含嵌入的零字节。 这意味着 UTF-8 字符串可以由 C 函数(如 strcpy())处理,并通过除了字符串结束标记之外无法处理零字节的协议发送。
  3. 一串 ASCII 文本也是有效的 UTF-8 文本。
  4. UTF-8 相当紧凑; 大多数常用字符可以用一两个字节表示。
  5. 如果字节损坏或丢失,则可以确定下一个 UTF-8 编码代码点的开始并重新同步。 随机 8 位数据也不太可能看起来像有效的 UTF-8。
  6. UTF-8 是一种面向字节的编码。 编码指定每个字符由一个或多个字节的特定序列表示。 这避免了在整数和面向字的编码(如 UTF-16 和 UTF-32)中可能出现的字节顺序问题,其中字节序列因编码字符串的硬件而异。


参考

Unicode Consortium 站点 有字符表、词汇表和 Unicode 规范的 PDF 版本。 为一些困难的阅读做好准备。 网站上还提供了 Unicode 起源和发展的年表

在 Computerphile Youtube 频道上,Tom Scott 简要 讨论了 Unicode 和 UTF-8 的历史(9 分 36 秒)。

为了帮助理解该标准,Jukka Korpela 编写了 介绍性指南 以阅读 Unicode 字符表。

另一篇 好的介绍性文章 由 Joel Spolsky 撰写。 如果这篇介绍没有让你清楚,你应该在继续之前尝试阅读这篇替代文章。

维基百科条目通常很有帮助; 例如,参见“字符编码”和UTF-8的条目。


Python 的 Unicode 支持

现在您已经了解了 Unicode 的基础知识,我们可以看看 Python 的 Unicode 特性。

字符串类型

从 Python 3.0 开始,语言的 str 类型包含 Unicode 字符,这意味着使用 "unicode rocks!"'unicode rocks!' 或三引号字符串语法创建的任何字符串都存储为 Unicode。

Python 源代码的默认编码是 UTF-8,因此您可以简单地在字符串文字中包含一个 Unicode 字符:

try:
    with open('/tmp/input.txt', 'r') as f:
        ...
except OSError:
    # 'File not found' error message.
    print("Fichier non trouvé")

旁注:Python 3 还支持在标识符中使用 Unicode 字符:

répertoire = "/tmp/records.log"
with open(répertoire, "w") as f:
    f.write("test\n")

如果您无法在编辑器中输入特定字符或出于某种原因想要仅保留源代码 ASCII,您还可以在字符串文字中使用转义序列。 (根据您的系统,您可能会看到实际的大写字母 - delta 字形而不是 au 转义符。)

>>> "\N{GREEK CAPITAL LETTER DELTA}"  # Using the character name
'\u0394'
>>> "\u0394"                          # Using a 16-bit hex value
'\u0394'
>>> "\U00000394"                      # Using a 32-bit hex value
'\u0394'

此外,可以使用 bytesdecode() 方法创建字符串。 此方法采用 encoding 参数,例如 UTF-8,以及可选的 errors 参数。

errors 参数指定无法根据编码规则转换输入字符串时的响应。 此参数的合法值为 'strict'(引发 UnicodeDecodeError 异常)、'replace'(使用 U+FFFDREPLACEMENT CHARACTER)、'ignore'(仅将字符从 Unicode 结果中删除),或 'backslashreplace'(插入 \xNN 转义序列)。 以下示例显示了差异:

>>> b'\x80abc'.decode("utf-8", "strict")  
Traceback (most recent call last):
    ...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
  invalid start byte
>>> b'\x80abc'.decode("utf-8", "replace")
'\ufffdabc'
>>> b'\x80abc'.decode("utf-8", "backslashreplace")
'\\x80abc'
>>> b'\x80abc'.decode("utf-8", "ignore")
'abc'

编码被指定为包含编码名称的字符串。 Python 有大约 100 种不同的编码; 有关列表,请参阅 标准编码 中的 Python 库参考。 一些编码有多个名称; 例如,'latin-1''iso_8859_1''8859' 都是相同编码的同义词。

也可以使用 chr() 内置函数创建单字符 Unicode 字符串,该函数接受整数并返回包含相应代码点的长度为 1 的 Unicode 字符串。 反向操作是内置的 ord() 函数,它接受一个字符的 Unicode 字符串并返回代码点值:

>>> chr(57344)
'\ue000'
>>> ord('\ue000')
57344

转换为字节

bytes.decode() 的相反方法是 str.encode(),它返回 Unicode 字符串的 bytes 表示,以请求的 编码]编码

errors 参数与 decode() 方法的参数相同,但支持更多可能的处理程序。 除了 'strict''ignore''replace'(在这种情况下插入一个问号而不是不可编码的字符),还有 'xmlcharrefreplace'(插入 XML 字符引用)、backslashreplace(插入 \uNNNN 转义序列)和 namereplace(插入 \N{...} 转义序列)。

以下示例显示了不同的结果:

>>> u = chr(40960) + 'abcd' + chr(1972)
>>> u.encode('utf-8')
b'\xea\x80\x80abcd\xde\xb4'
>>> u.encode('ascii')  
Traceback (most recent call last):
    ...
UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
  position 0: ordinal not in range(128)
>>> u.encode('ascii', 'ignore')
b'abcd'
>>> u.encode('ascii', 'replace')
b'?abcd?'
>>> u.encode('ascii', 'xmlcharrefreplace')
b'&#40960;abcd&#1972;'
>>> u.encode('ascii', 'backslashreplace')
b'\\ua000abcd\\u07b4'
>>> u.encode('ascii', 'namereplace')
b'\\N{YI SYLLABLE IT}abcd\\u07b4'

用于注册和访问可用编码的低级例程可在 codecs 模块中找到。 实现新编码还需要了解 codecs 模块。 然而,这个模块返回的编码和解码函数通常比较低级而不舒服,编写新的编码是一项专门的任务,因此本 HOWTO 不会涉及该模块。


Python 源代码中的 Unicode 文字

在 Python 源代码中,可以使用 \u 转义序列编写特定的 Unicode 代码点,后跟四个给出代码点的十六进制数字。 \U 转义序列类似,但需要八个十六进制数字,而不是四个:

>>> s = "a\xac\u1234\u20ac\U00008000"
... #     ^^^^ two-digit hex escape
... #         ^^^^^^ four-digit Unicode escape
... #                     ^^^^^^^^^^ eight-digit Unicode escape
>>> [ord(c) for c in s]
[97, 172, 4660, 8364, 32768]

对大于 127 的代码点使用转义序列在小剂量下是没问题的,但是如果您使用许多重音字符,就像在包含法语或其他一些使用重音的语言的消息的程序中一样,这会变得很烦人。 您还可以使用 chr() 内置函数组合字符串,但这更加乏味。

理想情况下,您希望能够以您的语言的自然编码编写文字。 然后,您可以使用您最喜欢的编辑器编辑 Python 源代码,该编辑器会自然地显示重音字符,并在运行时使用正确的字符。

默认情况下,Python 支持以 UTF-8 编写源代码,但如果您声明正在使用的编码,则几乎可以使用任何编码。 这是通过在源文件的第一行或第二行包含一个特殊注释来完成的:

#!/usr/bin/env python
# -*- coding: latin-1 -*-

u = 'abcdé'
print(ord(u[-1]))

该语法的灵感来自 Emacs 用于指定文件局部变量的符号。 Emacs 支持许多不同的变量,但 Python 仅支持“编码”。 -*- 符号向 Emacs 表明该注释是特殊的; 它们对 Python 没有意义,而是一种约定。 Python 在注释中查找 coding: namecoding=name

如果您不包含这样的注释,则使用的默认编码将是前面提到的 UTF-8。 另见 PEP 263 了解更多信息。


Unicode 属性

Unicode 规范包括一个有关代码点的信息数据库。 对于每个定义的代码点,信息包括字符的名称、类别、数值(如果适用)(对于表示数字概念的字符,如罗马数字、分数如三分之一和五分之四等)。 还有显示相关的属性,比如如何在双向文本中使用代码点。

下面的程序显示关于几个字符的一些信息,并打印一个特定字符的数值:

import unicodedata

u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)

for i, c in enumerate(u):
    print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")
    print(unicodedata.name(c))

# Get numeric value of second character
print(unicodedata.numeric(u[1]))

运行时,这会打印:

0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
1 0bf2 No TAMIL NUMBER ONE THOUSAND
2 0f84 Mn TIBETAN MARK HALANTA
3 1770 Lo TAGBANWA LETTER SA
4 33af So SQUARE RAD OVER S SQUARED
1000.0

类别代码是描述字符性质的缩写。 这些被分组为“字母”、“数字”、“标点符号”或“符号”等类别,而这些类别又分为子类别。 从上面的输出中获取代码,'Ll' 表示“字母,小写”,'No' 表示“数字,其他”,'Mn' 表示“标记,非空格”,[ X148X] 是“符号,其他”。 有关类别代码列表,请参阅 Unicode 字符数据库文档 通用类别值部分。


比较字符串

Unicode 为比较字符串增加了一些复杂性,因为同一组字符可以由不同的代码点序列表示。 例如,像“ê”这样的字母可以表示为单个代码点 U+00EA,或表示为 U+0065 U+0302,即“e”的代码点后跟“COMBINING CIRCUMFLEX ACCENT”的代码点. 这些将在打印时产生相同的输出,但一个是长度为 1 的字符串,另一个是长度为 2 的字符串。

一种不区分大小写比较的工具是 casefold() 字符串方法,该方法根据 Unicode 标准描述的算法将字符串转换为不区分大小写的形式。 该算法对诸如德语字母“ß”(代码点 U+00DF)之类的字符进行了特殊处理,它变成了一对小写字母“ss”。

>>> street = 'Gürzenichstraße'
>>> street.casefold()
'gürzenichstrasse'

第二个工具是 unicodedata 模块的 normalize() 函数,该函数将字符串转换为几种正常形式之一,其中字母后跟组合字符被替换为单个字符。 normalize() 可用于执行字符串比较,如果两个字符串使用不同的组合字符,则不会错误地报告不等式:

import unicodedata

def compare_strs(s1, s2):
    def NFD(s):
        return unicodedata.normalize('NFD', s)

    return NFD(s1) == NFD(s2)

single_char = 'ê'
multiple_chars = '\N{LATIN SMALL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
print('length of first string=', len(single_char))
print('length of second string=', len(multiple_chars))
print(compare_strs(single_char, multiple_chars))

运行时,输出:

$ python3 compare-strs.py
length of first string= 1
length of second string= 2
True

normalize() 函数的第一个参数是一个字符串,给出所需的规范化形式,可以是“NFC”、“NFKC”、“NFD”和“NFKD”之一。

Unicode 标准还指定了如何进行无大小写比较:

import unicodedata

def compare_caseless(s1, s2):
    def NFD(s):
        return unicodedata.normalize('NFD', s)

    return NFD(NFD(s1).casefold()) == NFD(NFD(s2).casefold())

# Example usage
single_char = 'ê'
multiple_chars = '\N{LATIN CAPITAL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'

print(compare_caseless(single_char, multiple_chars))

这将打印 True。 (为什么 NFD() 被调用了两次? 因为有几个字符使 casefold() 返回一个非规范化的字符串,所以结果需要再次规范化。 有关讨论和示例,请参阅 Unicode 标准的第 3.13 节。)


Unicode 正则表达式

re 模块支持的正则表达式可以以字节或字符串形式提供。 某些特殊字符序列(例如 \d\w)具有不同的含义,具体取决于模式是以字节还是字符串形式提供的。 例如,\d 将匹配字节中的字符 [0-9],但在字符串中将匹配 'Nd' 类别中的任何字符。

此示例中的字符串具有用泰语和阿拉伯数字书写的数字 57:

import re
p = re.compile(r'\d+')

s = "Over \u0e55\u0e57 57 flavours"
m = p.search(s)
print(repr(m.group()))

执行时,\d+ 会匹配泰语数字并打印出来。 如果您将 re.ASCII 标志提供给 compile(),则 \d+ 将匹配子字符串“57”。

类似地,\w 匹配多种 Unicode 字符,但仅 [a-zA-Z0-9_] 以字节为单位,或者如果提供 re.ASCII,并且 \s 将匹配任一 Unicode空白字符或 [ \t\n\r\f\v]


参考

关于 Python 的 Unicode 支持的一些很好的替代讨论是:

str 类型在 Python 库参考 文本序列类型 — str 中进行了描述。

unicodedata 模块的文档。

codecs 模块的文档。

Marc-André Lemburg 在 EuroPython 2002 上做了一个题为“Python 和 Unicode”(PDF 幻灯片) 的演讲 。 这些幻灯片很好地概述了 Python 2 的 Unicode 功能的设计(其中 Unicode 字符串类型称为 unicode,文字以 u 开头)。


读取和写入 Unicode 数据

一旦您编写了一些处理 Unicode 数据的代码,下一个问题就是输入/输出。 你如何将Unicode字符串放入你的程序中,你如何将Unicode转换成适合存储或传输的形式?

根据您的输入源和输出目的地,您可能不需要做任何事情; 您应该检查应用程序中使用的库是否原生支持 Unicode。 例如,XML 解析器经常返回 Unicode 数据。 许多关系数据库还支持 Unicode 值列,并且可以从 SQL 查询返回 Unicode 值。

Unicode 数据在写入磁盘或通过套接字发送之前通常会转换为特定的编码。 可以自己完成所有工作:打开一个文件,从中读取一个 8 位字节对象,然后使用 bytes.decode(encoding) 转换字节。 但是,不推荐手动方法。

一个问题是编码的多字节性质; 一个 Unicode 字符可以用几个字节表示。 如果要以任意大小的块(例如 1024 或 4096 字节)读取文件,则需要编写错误处理代码来捕获在末尾仅读取编码单个 Unicode 字符的部分字节的情况一块。 一种解决方案是将整个文件读入内存,然后执行解码,但这会阻止您处理非常大的文件; 如果您需要读取 2 GiB 的文件,则需要 2 GiB 的 RAM。 (更多,真的,因为至少有一段时间你需要在内存中同时拥有编码字符串和它的 Unicode 版本。)

解决方案是使用低级解码接口来捕获部分编码序列的情况。 实现这一点的工作已经为您完成:内置的 open() 函数可以返回一个类文件对象,该对象假定文件的内容采用指定的编码,并接受 Unicode 参数用于诸如如 read()write()。 这通过 open()'s encodingerrors 参数起作用,这些参数的解释就像 str.encode()bytes.decode()

因此,从文件中读取 Unicode 很简单:

with open('unicode.txt', encoding='utf-8') as f:
    for line in f:
        print(repr(line))

也可以在更新模式下打开文件,允许读取和写入:

with open('test', encoding='utf-8', mode='w+') as f:
    f.write('\u4500 blah blah blah\n')
    f.seek(0)
    print(repr(f.readline()[:1]))

Unicode 字符 U+FEFF 用作字节顺序标记 (BOM),通常作为文件的第一个字符写入,以帮助自动检测文件的字节顺序。 某些编码(例如 UTF-16)期望在文件开头出现 BOM; 当使用这种编码时,BOM 将自动作为第一个字符写入,并在读取文件时静默删除。 这些编码有多种变体,例如用于 little-endian 和 big-endian 编码的 'utf-16-le' 和 'utf-16-be',它们指定一种特定的字节顺序并且不跳过 BOM。

在某些地区,在 UTF-8 编码文件的开头使用“BOM”也是惯例; 该名称具有误导性,因为 UTF-8 不依赖于字节顺序。 该标记只是声明文件以 UTF-8 编码。 要读取此类文件,请使用“utf-8-sig”编解码器自动跳过标记(如果存在)。

Unicode 文件名

当今常用的大多数操作系统都支持包含任意 Unicode 字符的文件名。 通常这是通过将 Unicode 字符串转换为某种因系统而异的编码来实现的。 今天,Python 趋向于使用 UTF-8:MacOS 上的 Python 已经在多个版本中使用了 UTF-8,而 Python 3.6 在 Windows 上也改用了 UTF-8。 在 Unix 系统上,只有设置了 LANGLC_CTYPE 环境变量,才会有文件系统编码; 如果还没有,默认编码又是 UTF-8。

sys.getfilesystemencoding() 函数返回要在当前系统上使用的编码,以防您想手动进行编码,但没有太多理由费心。 打开文件进行读取或写入时,通常只需提供 Unicode 字符串作为文件名,它就会自动转换为正确的编码:

filename = 'filename\u4500abc'
with open(filename, 'w') as f:
    f.write('blah\n')

os 模块中的函数,例如 os.stat() 也将接受 Unicode 文件名。

os.listdir() 函数返回文件名,这引发了一个问题:它应该返回文件名的 Unicode 版本,还是应该返回包含编码版本的字节? os.listdir() 两者都可以,具体取决于您提供的目录路径是字节还是 Unicode 字符串。 如果您将 Unicode 字符串作为路径传递,则文件名将使用文件系统的编码进行解码并返回 Unicode 字符串列表,而传递字节路径将以字节形式返回文件名。 例如,假设默认文件系统编码为 UTF-8,运行以下程序:

fn = 'filename\u4500abc'
f = open(fn, 'w')
f.close()

import os
print(os.listdir(b'.'))
print(os.listdir('.'))

将产生以下输出:

$ python listdir-test.py
[b'filename\xe4\x94\x80abc', ...]
['filename\u4500abc', ...]

第一个列表包含 UTF-8 编码的文件名,第二个列表包含 Unicode 版本。

请注意,在大多数情况下,您应该坚持使用带有这些 API 的 Unicode。 字节 API 应仅用于可能存在不可解码文件名的系统; 现在几乎只有 Unix 系统。


编写可识别 Unicode 的程序的技巧

本节提供了一些关于编写处理 Unicode 的软件的建议。

最重要的提示是:

软件应该只在内部处理 Unicode 字符串,尽快解码输入数据并只在最后对输出进行编码。


如果您尝试编写同时接受 Unicode 和字节字符串的处理函数,您会发现您的程序在组合这两种不同类型的字符串的任何地方都容易受到错误的影响。 没有自动编码或解码:如果你这样做 str + bytes,一个 TypeError 将被引发。

当使用来自 Web 浏览器或其他不受信任来源的数据时,常用的技术是在生成的命令行中使用字符串或将其存储在数据库中之前检查字符串中的非法字符。 如果您这样做,请小心检查解码的字符串,而不是编码的字节数据; 一些编码可能具有有趣的特性,例如不是双射的或不完全兼容 ASCII。 如果输入数据还指定了编码,则尤其如此,因为攻击者可以选择一种巧妙的方式将恶意文本隐藏在编码的字节流中。

在文件编码之间转换

StreamRecoder 类可以在编码之间透明地转换,获取以编码 #1 返回数据的流,并且表现得像以编码 #2 返回数据的流。

例如,如果您有一个使用 Latin-1 格式的输入文件 f,您可以用 StreamRecoder 包装它以返回以 UTF-8 编码的字节:

new_f = codecs.StreamRecoder(f,
    # en/decoder: used by read() to encode its results and
    # by write() to decode its input.
    codecs.getencoder('utf-8'), codecs.getdecoder('utf-8'),

    # reader/writer: used to read and write to the stream.
    codecs.getreader('latin-1'), codecs.getwriter('latin-1') )

未知编码的文件

如果您需要对文件进行更改,但不知道文件的编码,该怎么办? 如果您知道编码与 ASCII 兼容并且只想检查或修改 ASCII 部分,您可以使用 surrogateescape 错误处理程序打开文件:

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

# make changes to the string 'data'

with open(fname + '.new', 'w',
          encoding="ascii", errors="surrogateescape") as f:
    f.write(data)

surrogateescape 错误处理程序会将任何非 ASCII 字节解码为从 U+DC80 到 U+DCFF 的特殊范围内的代码点。 当 surrogateescape 错误处理程序用于对数据进行编码并将其写回时,这些代码点将转回相同的字节。


参考

Mastering Python 3 Input/Output 的一部分,David Beazley 在 PyCon 2010 上的演讲,讨论了文本处理和二进制数据处理。

Marc-André Lemburg 的演讲“Writing Unicode-aware Applications in Python” PDF 幻灯片讨论了字符编码问题以及如何国际化和本地化应用程序。 这些幻灯片仅涵盖 Python 2.x。

The Guts of Unicode in Python 是 Benjamin Peterson 在 PyCon 2013 上的演讲,讨论了 Python 3.3 中的内部 Unicode 表示。


致谢

本文档的初稿由 Andrew Kuchling 撰写。 此后,Alexander Belopolsky、Georg Brandl、Andrew Kuchling 和 Ezio Melotti 对其进行了进一步修订。

感谢以下指出本文错误或提出建议的人:Éric Araujo、Nicholas Bastin、Nick Coghlan、Marius Gedminas、Kent Johnson、Ken Krugler、Marc-André Lemburg、Martin von Löwis、Terry J. Reedy、Serhiy Storchaka、Eryk Sun、Chad Whitacre、Graham Wideman。