目前,我有 Python 2.7 代码接收 <str>
通过套接字连接的对象。在整个代码中,我们使用 <str>
对象、比较等。为了转换为 Python 3,我发现套接字连接现在返回 <bytes>
需要我们将所有文字更改为类似 b'abc'
的对象进行字面比较等。这需要大量工作,虽然很明显为什么要在 Python 3 中进行此更改,但我很好奇是否有任何更简单的解决方法。
假设我收到 <bytes> b'\xf2a27'
通过套接字连接。有没有简单的方法来转换这些 <bytes>
进入<str>
在 Python 3.6 中具有相同转义的对象?我自己研究了一些解决方案,但无济于事。
a = b'\xf2a27'.decode('utf-8', errors='backslashescape')
以上产量 '\\xf2a27'
与 len(a) = 7
而不是原来的 len(b'\xf2a27') = 3
.索引也是错误的,这是行不通的,但它似乎正朝着正确的方向前进。
a = b'\xf2a27'.decode('latin1')
以上产量 'òa27'
其中包含我想避免的 Unicode 字符。虽然在这种情况下 len(a) = 5
和类似 a[0] == '\xf2'
的比较工作,但如果可能的话,我希望在表示中对信息进行转义。
我是否缺少更优雅的解决方案?
最佳答案
您确实必须考虑收到的数据代表什么,而 Python 3 在这方面发挥了重要作用。实际上表示字节集合的字节串与(抽象,unicode)字符的字符串之间存在重要区别。
如果每条数据可以有不同的表示,您可能需要单独考虑它们。
让我们以 b'\xf2a27'
为例,您从套接字接收到的原始格式只是一个 4 字节的字符串:0xf2
, 0x61
、0x32
、0x37
(十六进制)或 242
、97
、50
, 55
十进制。
假设您实际上需要其中的 4 个字节。您可以将其保留为字节字符串,也可以将其转换为字节的
list
或tuple
,如果这对您更好的话:raw_bytes = b'\xf2a27' list_of_bytes = list(raw_bytes) tuple_of_bytes = tuple(raw_bytes) if raw_bytes == b'\xf2a27': pass if list_of_bytes == [0xf2, 0x61, 0x32, 0x37]: pass if tuple_of_bytes == (0xf2, 0x61, 0x32, 0x37): pass
假设这实际上表示一个 32 位整数,在这种情况下,您应该将其转换为 Python
int
。选择它是以小字节序还是大字节序编码,并确保您选择了正确的有符号和无符号字节顺序。raw_bytes = b'\xf2a27' signed_little_endian, = struct.unpack('<i', raw_bytes) signed_little_endian = int.from_bytes(raw_bytes, byteorder='little', signed=True) unsigned_little_endian, = struct.unpack('<I', raw_bytes) unsigned_little_endian = int.from_bytes(raw_bytes, byteorder='little', signed=False) signed_big_endian, = struct.unpack('>i', raw_bytes) signed_big_endian = int.from_bytes(raw_bytes, byteorder='big', signed=True) unsigned_big_endian, = struct.unpack('>I', raw_bytes) unsigned_big_endian = int.from_bytes(raw_bytes, byteorder='big', signed=False) if signed_litte_endian == 926048754: pass
假设它实际上是文本。考虑一下它采用的编码方式。在您的情况下,它不能是 UTF-8,因为
b'\xf2'
将是一个无法正确解码为 UTF-8 的字节字符串。如果它是 latin1 a.k.a. iso8859-1 并且您确定它,那很好。raw_bytes = b'\xf2a27' character_string = raw_bytes.decode('iso8859-1') if character_string == '\xf2a27': pass
如果您选择的编码是正确的,那么在字符串中包含
'\xf2'
或'ò'
字符也是正确的。它仍然是一个单一的字符。'ò'
、'\xf2'
、'\u00f2'
和'\U000000f2'
只是 4 个不同在 (unicode) 字符串文字中表示相同单个 字符的方法。此外,len 将为 4,而不是 5。print(ord(character_string[0])) # will be 242 print(hex(ord(character_string[0]))) # will be 0xf2 print(len(character_string)) # will be 4
如果你实际观察到长度为 5,你可能观察到了错误的点。也许在将字符串编码为 UTF-8 或通过打印到 UTF-8 终端将其隐式编码为 UTF-8 之后。
注意更改默认 I/O 编码时输出到 shell 的字节数的差异:
PYTHONIOENCODING=UTF-8 python3 -c 'print(b"\xf2a27".decode("latin1"), end="")' | wc -c # will output 5 PYTHONIOENCODING=latin1 python3 -c 'print(b"\xf2a27".decode("latin1"), end="")' | wc -c # will output 4
理想情况下,您应该在将原始字节转换为它们代表的正确数据类型后执行比较。这使您的代码更具可读性和更易于维护。
作为一般经验法则,您应该始终在收到原始字节后立即将其转换为它们的实际(抽象)数据类型。然后将其保留在该抽象数据类型中,以便尽可能长时间地进行处理。如有必要,将其转换回输出的一些原始数据。
关于python - <bytes> 转义 <str> Python 3,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56641563/