1
qnnnnez 2016-01-02 18:19:00 +08:00
utf8
|
2
lcj2class 2016-01-02 18:55:13 +08:00 1
https://docs.python.org/3/howto/unicode.html#the-string-type
从这里看应该是 utf8 ,但是不确定,做了下面的实验, import pickle a = "中国人" with open("C:/Python33/a.txt", "bw") as f: pickle.dump(a, f) 然后找个能够查看二进制的编辑器,可以看到“中国人”被保存成了 utf-8 的 e4b8ad ,中 e59bbd ,国 e4baba ,人 |
4
timonwong 2016-01-02 19:25:47 +08:00
https://docs.python.org/3/howto/unicode.html#the-string-type
这里描述的是 source encoding (python 源文件默认 utf-8 编码) 这里是从 python3.3 开始的内部结构(PyUnicodeObject ) https://www.python.org/dev/peps/pep-0393/ 比较奇葩就是了 |
5
ruoyu0088 2016-01-02 20:59:29 +08:00 1
pickle 无法 dump 对象内存中的真正的值,可以使用 ctypes 直接访问对象内存:
import ctypes a = "中国人" import binascii binascii.hexlify(ctypes.string_at(id(a), a.__sizeof__())) 输出为: b'0200000000000000a073c93ba17f000003000000000000008a3d25ed4d04cf49a86ac83ba17f000000000000000000000000000000000000000000000000000000000000000000002d4efd56ba4e0000' 最后那部分是保存字符的,每个字符 2 个字节。 而 a = "abcd"的输出为: b'0200000000000000a073c93ba17f00000400000000000000c687538778475d60e57fc83ba17f000000000000000000006162636400' 每个字符一个字节。 |
8
congeec 2016-01-02 21:53:40 +08:00
@lcj2class 你这样测的是文件内容的编码,因为极有可能编译器是以 utf-8 编码打开文件的。把文件编码改成 GBK 再试一次看有没有异常
|
9
lcj2class 2016-01-02 21:59:00 +08:00
@ruoyu0088
https://github.com/python/cpython/blob/8a3d7944f8290f095e3c195dd4bafaed9e8e777a/Objects/unicodeobject.c 这段代码好长,先贴出来,后面慢慢看。 @congeec 不是的,我是以二进制的方式写的,和文件的编码没什么关系。 |
10
congeec 2016-01-02 22:02:49 +08:00
@lcj2class 你用 hex 编辑器看看文件内容,用 gbk 和 utf-8 分别写的时候 hex 值是不一样的。我说的不是 a.txt 而是你的源码文件
|
12
fy OP 我去,原来如此复杂。
感谢楼上几位给出的资料和方法。 python 源码中这个文件有 15665 行,实在是让人望而生畏。 @ruoyu0088 提到编码是自动选择的,我结合读文档的理解和一些测试, 再次进行了一遍求证,最后得到的结论是: python3 的字符串是根据输入确定编码,在 Latin1 , UTF16 、 UTF32 之间进行切换。 验证的过程是这样的: 1. 首先是 ucs2(utf16) 的情况 (据我所知 ucs2 与 u16 等价, ucs4 与 u32 等价,不知是否正确) In [60]: a = '中文' In [61]: binascii.hexlify(ctypes.string_at(id(a), a.__sizeof__())) Out[61]: b'010000003094e51d0200000056f1a772a8000000f447c7000000000000000000020000002d4e87650000' In [62]: binascii.hexlify('中文'.encode('utf-16')) Out[62]: b'fffe2d4e8765' 我们可以看到,“中文”两个字在编码为 utf16 之后首先是 fffe 这个头部,随后的 2d4e 8765 分别对应两个字,这与从内存中弄到的字符串形态是相同的。 2. 那么我们在文本中加入一个 UCS2 表示不了的字符串呢?会怎么样? In [64]: a = '\U000a1ffa 中文' In [65]: binascii.hexlify(ctypes.string_at(id(a), a.__sizeof__())) Out[65]: b'010000003094e51d03000000220bc0a8b000000000000000000000000000000000000000fa1f0a002d4e00008765000000000000' 我们可以看到, 2d4e 变成了 2d4e0000 , 8765 变成了 87650000 ,最后是 8 个 0 (一个 UCS4 字符)结尾。 而 fa1f0a00 是 000a1ffa 在内存中的形式(从右向左,每一个字节——即俩 HEX ——逐个倒装) 其实我觉得奇怪的地方在于, python 其实记录了文本的长度,为啥坚持 C 风格的字符串(末尾加\0 )? 看看这个字符串 encode 后的样子吧! In [66]: binascii.hexlify('\U000a1ffa 中文'.encode('utf-32')) Out[66]: b'fffe0000fa1f0a002d4e000087650000' In [67]: binascii.hexlify('\U000a1ffa 中文'.encode('utf-8')) Out[67]: b'f2a1bfbae4b8ade69687' 头部变成了 fffe0000 其他都一致。 3. 最后再看看单字节的字符串 In [68]: a = "\x9a\x9b" In [69]: binascii.hexlify(ctypes.string_at(id(a), a.__sizeof__())) Out[69]: b'010000003094e51d02000000b9bdd189a4000000000000000000000000000000000000009a9b00' ===== In [70]: a = "\x9a\x9b 中" In [71]: binascii.hexlify(ctypes.string_at(id(a), a.__sizeof__())) Out[71]: b'010000003094e51d03000000a4036e4ba8656164f44dc7000000000000000000030000009a009b002d4e0000' ===== In [72]: a = "\x9a\x9b 中\U000a1ffa" In [73]: binascii.hexlify(ctypes.string_at(id(a), a.__sizeof__())) Out[73]: b'010000003094e51d04000000776e9375b000005a000000000000000000000000000000009a0000009b0000002d4e0000fa1f0a0000000000' 果然不出所料。 说起来\U0000000 是一个比较少用的语法,专门用来转义 UCS4 的。 要不是前段时间搞了 tinyre 这个项目,我肯定是弄不出这样的字符的…… 顺便宣传一下在下最近这个项目,一个正则引擎: https://github.com/fy0/tinyre |
13
ruoyu0088 2016-01-03 09:20:03 +08:00
末尾加\0 是为了和 C 语言兼容,这样可以直接把字符串的地址传递给 C 语言的函数处理。
|
14
pynix 2016-01-03 12:20:31 +08:00
utf8
|
15
pynix 2016-01-03 12:22:05 +08:00
虚拟机层面 3.0 开始使用 Unicode ,后来为了性能优化又改成 utf8 了,好像是 3.3 来着。
|
16
lcj2class 2016-01-03 13:29:01 +08:00
|
17
fy OP @ruoyu0088 也是吧,不过也只能管一部分。因为 python 字符串内部可以有\0 。总体来说付出一个字的位置还是值得的。
但想想还是很蛋疼啊,比如经常有人掺杂一两个诡异的字符在纯英文文本里面,整个文本就会被拉长 2-4 倍不等。 不过我想最常用的字符集也就是 UCS2 了吧,所以还能够接受的样子。 |
18
fy OP 对了,其实将\0 当作字符串末尾我觉得不是一个好的设计。
python 在写扩展的时候,字符串的姿势有好几种,包括这种直接的\0 为末尾,中间不能有\0 的字符串,以及给出长度的字符串等等。 |
19
qnnnnez 2016-01-12 19:14:56 +08:00 via iPhone
看了下代码,发现确实是 UCS1, UCS2, UCS4 三种。之前听许多人说是 utf8 ,就想当然地以为是 utf8 了。现在想想,如果用变长编码,那还会有许多问题。为自己之前的答案道歉。
|