在Python> = 3.3中,为了帮助解决Unicode编码和解码问题,我希望能够从Python代码中检查存储在字符串中的实际内部数据。我该怎么做?
有一个str.encode()方法,它返回一个字节表示,但通常这是一个由特定编解码器(由“encoding”参数选择)转换的字节序列,而不是存储在其中的实际原始字节str对象。
有一个“unicode_internal”编码选项,但它已被弃用,并且不清楚它是否在3.3中返回真实的内部数据(如何组织?),或者是否有一些翻译。
PEP 393描述了Unicode数据的内部结构,从中可以看出从Python访问它需要报告字符串类型(1/2/4字节),表示(ASCII / compact)以及字节数组包含字符串内容,(我认为其格式为ASCII,UCS1,2或4)。我没有找到在Python中提供此访问权限的str类型的方法。
还有其他方法吗?也许是一种使用struct的聪明方法?或者是一个暴露这些字符串内部的C库?
更新2014-03-13:
感谢所有回复过关于为什么人们不想访问字符串内部结构的建议。对于普通的Python程序来说,这肯定是有效的建议。
尽管如此,我的问题是:如何做到这一点?
扩展基本原理:它是为了解决编码 - 解码问题,其中一个函数(在某些库中)可能创建并返回一个str,另一个函数(可能在某些其他库中)应该做某事与那个str。
我想检查那个中间str的确切内容,(即:我想将问题空间分成两半),并且这样做而不引入另一个变量,即使用一个或另一个python函数将该数据转换为某些其他形式(如带转义序列的ASCII)。
在其他原因中,我想知道确切的内部数据,以防另一个库中的一个实际上对内部数据格式敏感。所述库可能用C语言编写,可以访问该数据,并且处理不正确。
此外,确实应该将str作为一系列代码点来处理,内部内部表示无关紧要。但是如果字符串处理中确实存在错误,我不希望被它误导,如果没有,我会相信没有。鉴于字符串库的复杂性,零错误将是一个相当大的成就。
那么:我如何检查字符串的内部结构?
答案 0 :(得分:1)
Python的内部字符串表示形式严格来说是一个内部实现细节,并且可能从一个版本的Python到下一个版本以及在操作系统上有所不同。由于问题指定的Python版本号> = 3.3,所以我假设我们正在谈论CPython(1/2/4字节字符表示),并使用id()给出内存地址的CPython实现细节。以下使用Ubuntu 19.10的系统CPython 3.7.5。
from ctypes import string_at
from sys import getsizeof
from binascii import hexlify
a = "ABCDE"
print(hexlify(string_at(id(a), getsizeof(a))))
输出:
b'0100000000000000c0988500000000000500000000000000625866dab454b033e
50064016c006d010000000000000000414243444500'
您可以在十六进制的末尾看到“ ABCDE”,从41到45递增。如果从Unicode代码点范围128-255向该字符串添加了字符,例如0xA2处的分号,则“¢ ABCDE”,该字符仍可以用单个字节表示,因此CPython会这样做,尽管由于某些原因,字符串前面的null行会增加:
b'0200000000000000c09885000000000006000000000000003b7ac7a960368ad4a
4005a006501650200000000000000000000000000000000000000000000000000
00000000000000a2414243444500'
如果将大于Unicode 255的字符添加到字符串中,例如0x153的oe-ligature“ –ABCDE”,则整个字符串将转换为每个字符两个字节,“ –”为小尾数“ 5301”,“ A”为“ 4100”,依此类推:
b'0200000000000000c0988500000000000600000000000000e50dd134c7e9b87ca
83d22c59341424300000000000000000000000000000000000000000000000000
000000000000005301410042004300440045000000'
答案 1 :(得分:0)
Python中的Unicode字符串应被视为Unicode代码点序列。如何在内部表示这对编码和解码问题完全无关紧要。
您可以通过对字符串的各个字符使用ord()
函数来访问Unicode代码点的数值:
>>> list(map(ord, "abc €"))
[97, 98, 99, 32, 8364]
我不认为这对调试编码问题(或其他任何问题)特别有用,但它可能会在概念上阐明Unicode字符串的含义。
答案 2 :(得分:0)
内部切换到更节省空间的unicode值introduced by PEP-393存储空间仅用于性能原因。
因此,它们对Python代码中的编码和解码到unicode str
值的工作方式没有任何影响。从Python访问内部表示时绝对没有无意义。字符A
可以存储为41
,4100
或41000000
,具体取决于字符串中最高代码点所需的空间大小,但它仍会被编码为ASCII,Latin-1或UTF-8中的41
。
除非您正在编写必须处理此内部表示的C扩展,否则完全不需要担心Python实际存储数据的方式。
要调试编码或解码问题,我会使用ascii()
function仅使用ASCII代码点和Python字符串文字转义来表示字符串,或者您可以使用ord()
function将单个字符转换为每个代码点的整数。
对于字节值,binascii.hexlify()
function也可以很方便地将一系列字节快速转换为十六进制表示。