python - <bytes> 转义 <str> Python 3

标签 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>进入<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, 0x610x320x37(十六进制)或 2429750, 55 十进制。

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

    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
    
  2. 假设这实际上表示一个 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
    
  3. 假设它实际上是文本。考虑一下它采用的编码方式。在您的情况下,它不能是 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/

相关文章:

python - 对两列求和,在MapReduce中计算最大值,最小值和平均值

python - 从多个列中获取最近的观察和日期

python - 在 Python 中将两个列表拟合并对齐

python - 无法导入 Pandas 和 numpy

c++ - std::stringstream 的小数点?

python - 为什么 mySQL Python 查询不插入新条目?

将项目从 VS 2015 升级到 2017 后,Azure 上的 Python Flask 应用程序损坏

python - 使用 Python 实现 collat​​z 函数

java - 确定一对字符串中的任何一个是否结束另一个字符串

Python - 从字符串中删除前 5 个字符 - 如何获取新的较短字符串?