<bytes>转义了<str> Python 3

时间:2019-06-18 03:55:55

标签: python python-3.x string python-2.7 unicode

当前,我有 Python 2.7 代码,可通过套接字连接接收<str>对象。在所有代码中,我们都使用<str>对象,比较等。为了转换为 Python 3 ,我发现套接字连接现在返回<bytes>对象,这需要我们将所有文字更改为b'abc'进行文字比较等。这是很多工作,尽管很明显为什么在 Python 3 中进行了此更改,但我很好奇如果有更简单的解决方法。

说我通过套接字连接收到<bytes> b'\xf2a27'。是否有一种简单的方法可以将这些<bytes>转换为在{em> Python 3.6 中具有相同转义符的<str>对象?我本人已经研究了一些解决方案,但无济于事。

a = b'\xf2a27'.decode('utf-8', errors='backslashescape')

以上将产生'\\xf2a27'而不是原始len(a) = 7的{​​{1}}。索引编制也是错误的,这只是行不通,但似乎是正确的方法。

len(b'\xf2a27') = 3

上面产生a = b'\xf2a27'.decode('latin1') ,其中包含我要避免的Unicode字符。尽管在这种情况下,'òa27'和类似len(a) = 5的比较是可行的,但我希望尽可能地保留表示形式中的信息。

也许我缺少一个更优雅的解决方案?

1 个答案:

答案 0 :(得分:4)

您确实必须考虑接收到的数据代表什么,Python 3在这个方向上很重要。实际表示字节集合的字节字符串和(抽象,unicode)字符字符串之间存在重要区别。

如果每个数据都有不同的表示形式,则可能必须分别考虑它们。

让我们以b'\xf2a27'为例,它从套接字收到的原始格式只是一个4字节的字符串:0xf20x610x32,{十六进制的{1}}或十进制的0x372429750

  1. 假设您实际上需要4个字节。您可以将其保留为字节字符串,也可以将其转换为55list个字节,如果这样做更好的话:

    tuple
  2. 让我们说这实际上代表一个32位整数,在这种情况下,您应该将其转换为Python 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 。选择是以小端字节序还是大端字节序编码的,并确保选择正确的带符号或无符号。

    int
  3. 假设它实际上是文本。考虑一下它的编码方式。在您的情况下,它不能为UTF-8,因为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的字节字符串。如果它是latin1 a.k.a. iso8859-1,并且您确定可以,那么就可以了。

    b'\xf2'

    如果您选择的编码正确,则在字符串中包含raw_bytes = b'\xf2a27' character_string = raw_bytes.decode('iso8859-1') if character_string == '\xf2a27': pass '\xf2'字符也是正确的。它仍然是一个字符。 'ò''ò''\xf2''\u00f2'只是在(unicode)字符串文字中表示同一单个字符的4种不同方式。另外,len将为4,而不是5。

    '\U000000f2'

    如果您实际观察到长度为5,则可能是在错误的位置观察到的。也许在将字符串编码为UTF-8之后,或者通过打印到UTF-8终端隐式地将其编码为UTF-8。

    请注意更改默认I / O编码时输出到外壳的字节数的差异:

    print(ord(character_string[0]))       # will be 242
    print(hex(ord(character_string[0])))  # will be 0xf2
    
    print(len(character_string))          # will be 4
    

理想情况下, 之后,您应该执行比较,将原始字节转换为它们代表的正确数据类型。这使您的代码更具可读性,更易于维护。

作为一般经验法则,应始终在收到原始字节后立即将其转换为它们的实际(抽象)数据类型。然后将其保留在该抽象数据类型中,以便进行尽可能长的处理。如有必要,将其转换回输出的一些原始数据。