【问题标题】:Latin1 character values not displaying the same as in utf8Latin1 字符值的显示与 utf8 中的不同
【发布时间】:2013-12-11 01:43:45
【问题描述】:

对于 PYTHON 2.7(我拍摄了在 3 中使用 encode 的照片,现在很困惑......希望得到一些建议,如何在 python 3 中复制这个测试......)

对于欧元字符 (€),我使用 this tool 查找了它的 utf8 十六进制代码点。它说它是0x20AC。

对于 Latin1(再次使用 Python2 2.7),我使用 decode 来获取它的 Hex 代码点:

>>import unicodedata
>>p='€'
## notably x80 seems to correspond to [Windows CP1252 according to the link][2]
>>p.decode('latin-1') 
>>u'\x80'

然后我对他们两个都使用了这个打印语句,这就是我得到的:

对于 utf8:

>>> print unichr(0x20AC).encode('utf-8')
€

对于 latin-1:

>>> print unichr(0x80).encode('latin-1')
€

到底发生了什么?为什么编码为 utf-8 返回 '‚¬'?另外......似乎 Latin1 十六进制代码点可能与它们的 utf8 对应物不同(我有一位同事认为不同 - 说 Latin1 在这方面就像 ASCII)。但是不同代码点的存在似乎对我有不同的暗示……但是 python 2.7 读取Windows CP1252 'x80' 的原因对我来说是一个真正的谜……这是 latin-1 在 python 2.7 中的标准吗? ?

【问题讨论】:

  • 首先,欧元字符的 UTF-8 是 '\xE2\x82\xAC'。在 UTF-8 中,'\x20\xAC' 是一个空格,后面跟着一个非法字符。
  • 你是说我需要输入'\xe2\x82\xac'??这对我来说没有意义......或python。输入时出现“SyntaxError: invalid syntax”:>>> print unichr('\xe2\x82\xac').encode('utf-8')
  • 首先,你没有把引号放进去。其次,unichr 接受一个数字,而不是一个字符串,所以无论如何这都行不通。要将字节字符串转换为unicode,您需要使用decode 方法,并指定要转换的编码。所以,print '\xe2\x82\xac'.decode('utf-8').encode('utf-8') 会给你……正如你所期望的那样,正是你开始的。
  • 抱歉编辑。它说: SyntaxError: invalid syntax
  • 那你复制粘贴错了。如果我将确切的代码粘贴到我的 Python 2.7 解释器或像 this one 这样的在线解释器中,则没有 SyntaxError;它在带有 UTF-8 控制台的机器上打印出,在带有 CP-1252 控制台的机器上打印出€,等等。

标签: python unicode encoding utf-8 latin1


【解决方案1】:

你在这里有一些严重的误解。如果您还没有阅读 Python 2Python 3 的 Unicode HOWTO,您应该从那里开始。

首先,UTF-8 是 Unicode 到 8 位字节的编码。没有 UTF-8 代码点 0x20AC 这样的东西。有一个 Unicode 代码点 U+20AC,但在 UTF-8 中,它是三个字节:0xE20x820xAC


这就解释了你在这里的困惑:

为什么编码为 utf-8 返回 '‚¬'?

它没有。它返回字节字符串'\xE2\x82\xAC'。然后你 printed 到你的控制台。您的控制台可能在 CP-1252 中,因此它将这些字节解释为好像它们是 CP-1252,这给了您€


同时,当你写这个时:

p='€'

控制台没有提供 Python Unicode,它提供了 CP-1252 中的 Python 字节,Python 只是将其存储为字节。欧元符号的 CP-1252 是\x80。所以,这和打字是一样的:

p='\x80'

但在 Latin-1 中,\x80 不是欧元符号,它是一个不可见的控制字符,相当于 Unicode U+0080。因此,当您拨打p.decode('latin-1') 时,您会回复u'\x80'。这正是您所看到的。


您无法在 Python 3 中重现这一点的原因是,在 Python 3 中,str 和纯字符串文字是 Unicode 字符串,而不是字节字符串。所以,当你写这个时:

p='€'

... 控制台给 Python 一些字节,然后 Python 使用它为控制台猜测的字符集 (CP-1252) 自动将这些字节解码为 Unicode。所以,就相当于这样写:

p='\u20ac'

……或者这个:

p=b'\x80'.decode(sys.stdin.encoding)

另外,你一直说“十六进制代码点”来表示各种不同的东西,这些都没有任何意义。

代码点是一个 Unicode 概念。 Python 中的unicode 字符串是一个代码点序列。 strbytes 的序列,而不是代码点。十六进制只是表示数字的一种方式——十六进制数20AC0x20AC与十进制数8364相同,十六进制数0x80与十进制数@相同987654347@.

这个字节序列本身并没有作为文本的任何内在含义;它需要与编码结合才能有意义。根据编码的不同,一些代码点可能根本无法表示,而另一些可能需要 2 个或更多字节来表示。


最后:

另外...看起来 Latin1 十六进制代码点可能与对应的 utf8 代码点不同(我有一位同事认为不同 - 说 Latin1 在这方面就像 ASCII)。

Latin-1 是 ASCII 的超集。 Unicode 也是 Latin-1 的可打印子集的超集; U+FF 之前的一些 Unicode 字符(以及 U+7F 之前的所有可打印字符)以 UTF-8 编码为与代码点具有相同值的字节,但不是全部。 CP-1252 是 Latin-1 的可打印子集的不同超集。由于在 ASCII 或 Latin-1 中没有欧元符号,CP-1252 和 UTF-8 以不同方式表示它是完全合理的。

【讨论】:

  • 您的最后一段中有一个错误:Unicode,而不是 UTF-8,是 Latin-1 可打印部分的超集。
  • 什么是“Latin-1 的所有可打印部分”?我已经阅读了 python 2 How-To 十几次(即 esp "...Latin-1,也称为 ISO-8859-1,是一种类似的编码。Unicode 代码点 0-255 与拉丁文相同-1 值..")。我很抱歉错误地使用了“十六进制代码点”(是的,我指的是 Unicode 代码点),这就是我感到困惑的地方——Latin-1 Euro 字符的代码点与 utf 有何不同-8 字符,如果 utf8 是 Latin-1 的“超集”??
  • @jwodder:谢谢;我重写了它以使其更清晰,我希望。
  • @user14696:见the Wikipedia article。 Latin-1 实际上并没有定义 0x80-0x9F,因此 Unicode 代码点 U+80 与 Latin-1 0x80 相同并不是真的,因为 is 没有 Latin-1 0x80。但是 Unicode 将 0x80-09F 定义为保留的控制字符,所以这没什么大不了的。
  • @user14696:同时,即使是最后一段的原始版本也解释说拉丁语 1 中没有欧元符号。您没有使用 Latin-1,您使用的是 CP-1252,这是一个 Latin-1 扩展,它为 Latin-1 中不存在的字符定义(部分)0x80-0x9F,并且映射到整个地方在 Unicode 中。 CP-1252 0x80 为 U+20AC,; Latin-1 0x80 是 U+0080,一个保留的控制字符。
猜你喜欢
  • 2016-03-12
  • 2011-11-02
  • 2012-03-13
  • 1970-01-01
  • 1970-01-01
  • 2010-12-04
  • 1970-01-01
  • 2014-11-04
  • 2015-04-20
相关资源
最近更新 更多