二进制数据被写为字符串文字 - 如何将其转换回字节?

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 9cx = 78 十六进制)......如果你在这里查看:https://en.wikipedia.org/wiki/List_of_file_signatures,可以看出是zlip

所以,我使用 zlib 库对其进行了解码……好东西。