Tho*_*ind 4 python string unicode python-2.7 python-3.x
目前,我有通过套接字连接接收对象的Python 2.7代码。<str>在整个代码中,我们使用<str>对象、比较等。为了转换为Python 3,我发现套接字连接现在返回<bytes>对象,这要求我们更改所有文字,就像b'abc'进行文字比较等。这是虽然Python 3中进行此更改的原因很明显,但我很好奇是否有任何更简单的解决方法。
假设我<bytes> b'\\xf2a27'通过套接字连接接收。有没有一种简单的方法可以将它们转换<bytes>为在Python 3.6<str>中具有相同转义的对象?我自己研究了一些解决方案但无济于事。
a = b'\\xf2a27'.decode('utf-8', errors='backslashescape')\nRun Code Online (Sandbox Code Playgroud)\n\n上面的结果'\\\\xf2a27'是 withlen(a) = 7而不是原来的len(b'\\xf2a27') = 3. 索引也是错误的,这行不通,但看起来它正走在正确的道路上。
a = b'\\xf2a27'.decode('latin1')\nRun Code Online (Sandbox Code Playgroud)\n\n上面的结果'\xc3\xb2a27'包含我想避免的 Unicode 字符。虽然在这种情况下len(a) = 5,比较就像a[0] == '\\xf2'工作,但如果可能的话,我想在表示中保留信息转义。
我是否缺少更优雅的解决方案?
\n你确实必须考虑你收到的数据代表什么,Python 3 在这个方向上提出了一个强点。实际表示字节集合的字节字符串和(抽象、unicode)字符字符串之间存在重要区别。
\n\n如果每条数据可以有不同的表示形式,您可能必须单独考虑它们。
\n\n让我们以b\'\\xf2a27\'您从套接字收到的原始形式为例,它只是一个 4 字节的字符串:十六进制的0xf2, 0x61, 0x32,或十进制的, , , 。0x37242975055
假设您实际上想要其中的 4 个字节。您可以将其保留为字节字符串,也可以将其转换为字节list或tuple字节(如果这样对您来说更好):
raw_bytes = b\'\\xf2a27\'\n\nlist_of_bytes = list(raw_bytes)\n\ntuple_of_bytes = tuple(raw_bytes)\n\nif raw_bytes == b\'\\xf2a27\':\n pass\n\nif list_of_bytes == [0xf2, 0x61, 0x32, 0x37]:\n pass\n\nif tuple_of_bytes == (0xf2, 0x61, 0x32, 0x37):\n pass\nRun Code Online (Sandbox Code Playgroud)假设这实际上代表一个 32 位整数,在这种情况下您应该将其转换为 Python int。选择是以小端还是大端字节顺序编码,并确保选择正确的有符号和无符号之一。
raw_bytes = b\'\\xf2a27\'\n\nsigned_little_endian, = struct.unpack(\'<i\', raw_bytes)\nsigned_little_endian = int.from_bytes(raw_bytes, byteorder=\'little\', signed=True)\n\nunsigned_little_endian, = struct.unpack(\'<I\', raw_bytes)\nunsigned_little_endian = int.from_bytes(raw_bytes, byteorder=\'little\', signed=False)\n\nsigned_big_endian, = struct.unpack(\'>i\', raw_bytes)\nsigned_big_endian = int.from_bytes(raw_bytes, byteorder=\'big\', signed=True)\n\nunsigned_big_endian, = struct.unpack(\'>I\', raw_bytes)\nunsigned_big_endian = int.from_bytes(raw_bytes, byteorder=\'big\', signed=False)\n\nif signed_litte_endian == 926048754:\n pass\nRun Code Online (Sandbox Code Playgroud)假设它实际上是文本。考虑一下它采用的编码。在您的情况下,它不能是 UTF-8,因为b\'\\xf2\'字节字符串无法正确解码为 UTF-8。如果它是 latin1 又名 iso8859-1 并且您确定它,那就没问题。
raw_bytes = b\'\\xf2a27\'\n\ncharacter_string = raw_bytes.decode(\'iso8859-1\')\n\nif character_string == \'\\xf2a27\':\n pass\nRun Code Online (Sandbox Code Playgroud)\n\n如果您选择的编码是正确的,那么字符串中包含\'\\xf2\'or\'\xc3\xb2\'字符也是正确的。它仍然是单个字符。\'\xc3\xb2\'、\'\\xf2\'、\'\\u00f2\'和\'\\U000000f2\'只是在 (unicode) 字符串文字中表示相同单个字符的 4 种不同方式。另外,len 将为 4,而不是 5。
print(ord(character_string[0])) # will be 242\nprint(hex(ord(character_string[0]))) # will be 0xf2\n\nprint(len(character_string)) # will be 4\nRun Code Online (Sandbox Code Playgroud)\n\n如果您实际观察到的长度为 5,那么您可能在错误的点上观察到了它。也许将字符串编码为 UTF-8 或通过打印到 UTF-8 终端将其隐式编码为 UTF-8 之后。
\n\n请注意更改默认 I/O 编码时输出到 shell 的字节数的差异:
\n\nPYTHONIOENCODING=UTF-8 python3 -c \'print(b"\\xf2a27".decode("latin1"), end="")\' | wc -c\n# will output 5\n\nPYTHONIOENCODING=latin1 python3 -c \'print(b"\\xf2a27".decode("latin1"), end="")\' | wc -c\n# will output 4\nRun Code Online (Sandbox Code Playgroud)理想情况下,您应该在将原始字节转换为它们表示的正确数据类型之后执行比较。这使您的代码更具可读性并且更易于维护。
\n\n作为一般经验法则,您应该在收到原始字节后立即将其转换为其实际(抽象)数据类型。然后将其保留在抽象数据类型中以便尽可能长时间地进行处理。如有必要,将其转换回输出时的一些原始数据。
\n