【问题标题】:Why is the output of print in python2 and python3 different with the same string?为什么 python2 和 python3 中 print 的输出在同一个字符串中是不同的?
【发布时间】:2020-07-27 03:25:32
【问题描述】:

在python2中:

$ python2 -c 'print "\x08\x04\x87\x18"' | hexdump -C
00000000  08 04 87 18 0a                                    |.....|
00000005

在python3中:

$ python3 -c 'print("\x08\x04\x87\x18")' | hexdump -C
00000000  08 04 c2 87 18 0a                                 |......|
00000006

为什么这里有字节"\xc2"

编辑

我认为当字符串具有非 ascii 字符时,python3 会将字节 "\xc2" 附加到字符串中。 (正如@Ashraful Islam 所说)

那么如何在 python3 中避免这种情况呢?

【问题讨论】:

  • 还有其他例子吗?你能找到一个模式吗?

标签: python unicode utf-8


【解决方案1】:

考虑下面的sn-p代码:

import sys
for i in range(128, 256):
    sys.stdout.write(chr(i))

使用 Python 2 运行它并使用 hexdump -C 查看结果:

00000000  80 81 82 83 84 85 86 87  88 89 8a 8b 8c 8d 8e 8f  |................|

等等。没有惊喜;从0x800xff 的 128 个字节。

对 Python 3 做同样的事情:

00000000  c2 80 c2 81 c2 82 c2 83  c2 84 c2 85 c2 86 c2 87  |................|
...
00000070  c2 b8 c2 b9 c2 ba c2 bb  c2 bc c2 bd c2 be c2 bf  |................|
00000080  c3 80 c3 81 c3 82 c3 83  c3 84 c3 85 c3 86 c3 87  |................|
...
000000f0  c3 b8 c3 b9 c3 ba c3 bb  c3 bc c3 bd c3 be c3 bf  |................|

总结一下:

  • 0x800xbf 的所有内容都带有0xc2 前缀。
  • 0xc00xff 的所有内容都将第 6 位设置为零,并在前面加上 0xc3

那么,这里发生了什么?

在 Python 2 中,字符串是 ASCII,不进行任何转换。告诉它 在 0-127 ASCII 范围之外写一些东西,它说“okey-doke!”和 只写那些字节。很简单。

在 Python 3 中,字符串是 Unicode。当非 ASCII 字符 写,它们必须以某种方式编码。默认编码是 UTF-8。

那么,这些值是如何以 UTF-8 编码的?

0x800x7ff 的代码点编码如下:

110vvvvv 10vvvvvv

其中 11 个v 字符是代码点的位。

因此:

0x80                 hex
1000 0000            8-bit binary
000 1000 0000        11-bit binary
00010 000000         divide into vvvvv vvvvvv
11000010 10000000    resulting UTF-8 octets in binary
0xc2 0x80            resulting UTF-8 octets in hex

0xc0                 hex
1100 0000            8-bit binary
000 1100 0000        11-bit binary
00011 000000         divide into vvvvv vvvvvv
11000011 10000000    resulting UTF-8 octets in binary
0xc3 0x80            resulting UTF-8 octets in hex

这就是为什么你会在87 之前获得c2

如何在 Python 3 中避免这一切?使用bytes 类型。

【讨论】:

【解决方案2】:

Python 2 的默认字符串类型是字节字符串。字节字符串写入"abc",而Unicode 字符串写入u"abc"

Python 3 的默认字符串类型是 Unicode 字符串。字节字符串写成b"abc",而Unicode 字符串写成"abc"u"abc" 仍然有效)。由于有数百万个 Unicode 字符,因此将它们打印为字节需要编码(在您的情况下为 UTF-8),每个代码点需要多个字节。

首先在 Python 3 中使用一个字节字符串来获得与 Python 2 相同的类型。然后,因为 Python 3 的 print 需要 Unicode 字符串,所以使用 sys.stdout.buffer.write 写入需要字节字符串的原始 stdout 接口。

python3 -c 'import sys; sys.stdout.buffer.write(b"\x08\x04\x87\x18")'

请注意,如果写入文件,也会出现类似问题。对于无编码转换,以二进制模式打开文件'wb'并写入字节字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多