二进制数据被写为字符串文字 - 如何将其转换回字节?
Binary data gets written as string literal - how to convert it back to bytes?
我正在将压缩数据作为 bytes
类型写入黑盒 API(即我无法更改引擎盖下发生的事情)。当我取回该数据时,它以 string
类型返回,我无法使用通用 python 模块(zlib、bz2 等)
解压缩
更详细地说,部分问题是该字符串包含前导 'b'
,例如
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
(这是一个字符串类型)。
当我将其与原始二进制表示形式进行比较时,除了引号和前导 B 之外,它是相同的。
如果我尝试简单地转换回字节(例如使用 bytes
函数),它会包装整个内容并转义斜线,我得到如下内容:
b"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
问题是,是否可以将其转换回字节类型以便我可以解压缩它?如果是,怎么做?
我看到了一些不同的示例(例如 How to cast a string to bytes without encoding),但它们对我正在尝试的内容不太适用。
更新:
很多好的答案,谢谢大家!我希望我可以点击接受其中的多个。是的,正如你们中的许多人所指出的,它是 zlib 压缩的。这是设计使然,因为我们可以使用的 space 非常有限,如果可能的话,我们希望继续使用 JSON最后的选择)。
您可以通过选择除前两个 b'
和最后一个 '
字符之外的整个字符串来获取字符串中的字节。然后先将其转换为字节,然后解码回字符串。
举个例子:
str(bytes(bytes_string[2:-1], encoding), encoding)
其中:
bytes_string = "b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
编码是字节字符串中使用的编码(例如'UTF-8')
假设您的原始字符串类型为 str
,您有以下原始字符串(文字长度为 4 个转义码,而不是表示 1 个字节的实际转义码):
s = r"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
如果去掉开头的b'
和'
,可以使用latin1
编码转换为字节。 latin1
是 Unicode 代码点到字节值的 1:1 映射,因为前 256 个 Unicode 代码点代表 latin1
字符集:
>>> s[2:-1].encode('latin1')
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
现在这是一个字节字符串,但包含文字转义码。现在应用 unicode_escape
编码转换回实际代码点的 str
:
>>> s2 = b.decode('unicode_escape')
>>> s2
'x\x9c«V*HLÑÍÌKËW²RPJËÏOJ,Rª\x05\x00T\x83\x07b'
现在这是一个带有代码点的 Unicode 字符串,但我们仍然需要一个字节字符串。再次用 latin1
编码:
>>> b2 = s2.encode('latin1')
>>> b2
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
一步到位:
>>> s = r"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
>>> b = s[2:-1].encode('latin1').decode('unicode_escape').encode('latin1')
>>> b
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
这个示例数据似乎是一个 zlib 压缩的 JSON 字符串:
>>> import zlib,json
>>> json.loads(zlib.decompress(b))
{'pad-info': 'foobar'}
这是怎么回事:
黑盒服务器在发送前将字节串化。您需要获取表示字节的字符串并将其转换回字节。最简单的方法是使用抽象语法树库 (ast)。
import ast
import zlib
stringified_bytes = "b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
print(f"{type(stringified_bytes)}: {stringified_bytes}")
actual_bytes = ast.literal_eval(stringified_bytes)
print(f"{type(actual_bytes)}: {actual_bytes}")
answer = zlib.decompress(actual_bytes)
print(f"Answer: {answer}")
这里是一个运行的脚本:
(venv) [ttucker@zim Whosebug]$ python bin.py
<class 'str'>: b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
<class 'bytes'>: b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
Answer: b'{"pad-info": "foobar"}'
...这是非常有趣的东西...看起来他们还有另一个包含 JSON 的字节串。这像是黑客编码挑战之一吗?
顺便说一句,你有一个 zlib 文件
我知道这个是因为数据的开头两个字节是 78 9c
(x
= 78
十六进制)......如果你在这里查看:https://en.wikipedia.org/wiki/List_of_file_signatures,可以看出是zlip
所以,我使用 zlib 库对其进行了解码……好东西。
我正在将压缩数据作为 bytes
类型写入黑盒 API(即我无法更改引擎盖下发生的事情)。当我取回该数据时,它以 string
类型返回,我无法使用通用 python 模块(zlib、bz2 等)
更详细地说,部分问题是该字符串包含前导 'b'
,例如
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
(这是一个字符串类型)。
当我将其与原始二进制表示形式进行比较时,除了引号和前导 B 之外,它是相同的。
如果我尝试简单地转换回字节(例如使用 bytes
函数),它会包装整个内容并转义斜线,我得到如下内容:
b"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
问题是,是否可以将其转换回字节类型以便我可以解压缩它?如果是,怎么做?
我看到了一些不同的示例(例如 How to cast a string to bytes without encoding),但它们对我正在尝试的内容不太适用。
更新:
很多好的答案,谢谢大家!我希望我可以点击接受其中的多个。是的,正如你们中的许多人所指出的,它是 zlib 压缩的。这是设计使然,因为我们可以使用的 space 非常有限,如果可能的话,我们希望继续使用 JSON最后的选择)。
您可以通过选择除前两个 b'
和最后一个 '
字符之外的整个字符串来获取字符串中的字节。然后先将其转换为字节,然后解码回字符串。
举个例子:
str(bytes(bytes_string[2:-1], encoding), encoding)
其中:
bytes_string = "b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
编码是字节字符串中使用的编码(例如'UTF-8')
假设您的原始字符串类型为 str
,您有以下原始字符串(文字长度为 4 个转义码,而不是表示 1 个字节的实际转义码):
s = r"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
如果去掉开头的b'
和'
,可以使用latin1
编码转换为字节。 latin1
是 Unicode 代码点到字节值的 1:1 映射,因为前 256 个 Unicode 代码点代表 latin1
字符集:
>>> s[2:-1].encode('latin1')
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
现在这是一个字节字符串,但包含文字转义码。现在应用 unicode_escape
编码转换回实际代码点的 str
:
>>> s2 = b.decode('unicode_escape')
>>> s2
'x\x9c«V*HLÑÍÌKËW²RPJËÏOJ,Rª\x05\x00T\x83\x07b'
现在这是一个带有代码点的 Unicode 字符串,但我们仍然需要一个字节字符串。再次用 latin1
编码:
>>> b2 = s2.encode('latin1')
>>> b2
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
一步到位:
>>> s = r"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
>>> b = s[2:-1].encode('latin1').decode('unicode_escape').encode('latin1')
>>> b
b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
这个示例数据似乎是一个 zlib 压缩的 JSON 字符串:
>>> import zlib,json
>>> json.loads(zlib.decompress(b))
{'pad-info': 'foobar'}
这是怎么回事:
黑盒服务器在发送前将字节串化。您需要获取表示字节的字符串并将其转换回字节。最简单的方法是使用抽象语法树库 (ast)。
import ast
import zlib
stringified_bytes = "b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
print(f"{type(stringified_bytes)}: {stringified_bytes}")
actual_bytes = ast.literal_eval(stringified_bytes)
print(f"{type(actual_bytes)}: {actual_bytes}")
answer = zlib.decompress(actual_bytes)
print(f"Answer: {answer}")
这里是一个运行的脚本:
(venv) [ttucker@zim Whosebug]$ python bin.py
<class 'str'>: b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
<class 'bytes'>: b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'
Answer: b'{"pad-info": "foobar"}'
...这是非常有趣的东西...看起来他们还有另一个包含 JSON 的字节串。这像是黑客编码挑战之一吗?
顺便说一句,你有一个 zlib 文件
我知道这个是因为数据的开头两个字节是 78 9c
(x
= 78
十六进制)......如果你在这里查看:https://en.wikipedia.org/wiki/List_of_file_signatures,可以看出是zlip
所以,我使用 zlib 库对其进行了解码……好东西。