Python 二进制数据以字符串文字形式写入-如何将其转换回字节?

Python 二进制数据以字符串文字形式写入-如何将其转换回字节?,python,python-3.x,encoding,Python,Python 3.x,Encoding,我将压缩数据作为字节类型写入黑盒API,即我无法更改引擎盖下发生的事情。当我返回数据时,它将作为字符串类型返回,我无法使用通用python模块zlib、bz2等对其进行解压缩 更详细地说,部分问题在于该字符串包含前导的“b”,例如。 b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b' 这是一种字符串类型 当我将其与原始的二进制表示进行比较时,除了引号和前导B之外,它是相同的 如果我尝试简单地转换回

我将压缩数据作为字节类型写入黑盒API,即我无法更改引擎盖下发生的事情。当我返回数据时,它将作为字符串类型返回,我无法使用通用python模块zlib、bz2等对其进行解压缩

更详细地说,部分问题在于该字符串包含前导的“b”,例如。 b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b' 这是一种字符串类型

当我将其与原始的二进制表示进行比较时,除了引号和前导B之外,它是相同的

如果我尝试简单地转换回字节,例如,使用bytes函数,它会包装整个内容并转义斜杠,我得到如下结果:

bb'x\\x9c\\xabV*HL\\xd1\\xcd\\xccK\\xcbW\\xb2RPJ\\xcb\\xcfOJ,R\\xaa\\x05\\x00T\\x83\\x07b'

问题是,是否可以将其转换回字节类型,以便我可以解压缩它?如果是,怎么做

我已经看到了一些不同的例子,例如,对我正在尝试的东西不太合适

更新:


很多好答案,谢谢大家!我希望我能点击多个按钮。是的,正如你们很多人所注意到的,它是zlib压缩的。这是出于设计考虑,因为我们可以使用的空间非常有限,如果可能的话,我们希望继续使用JSON,因为zlib是任意选择的,只是为了获取二进制数据的怪癖,可能不是最终的选择。

您可以通过选择除前两个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”

您可以通过选择整个字符串(前两个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'和',可以使用拉丁1编码转换为字节。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_转义编码将实际代码点转换回str:

>>> s2 = b.decode('unicode_escape')
>>> s2
'x\x9c«V*HLÑÍÌKËW²RPJËÏOJ,Rª\x05\x00T\x83\x07b'
这现在是一个Unicode字符串,带有代码点,但我们仍然需要一个字节字符串。再次使用拉丁语1编码:

一步:

>>> 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'}
假设原始字符串为str类型,则具有以下原始字符串文字长度4个转义码,而不是表示1字节的实际转义码:

s = r"b'x\x9c\xabV*HL\xd1\xcd\xccK\xcbW\xb2RPJ\xcb\xcfOJ,R\xaa\x05\x00T\x83\x07b'"
如果删除前导的b'和',可以使用拉丁1编码转换为字节。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_转义编码将实际代码点转换回str:

>>> s2 = b.decode('unicode_escape')
>>> s2
'x\x9c«V*HLÑÍÌKËW²RPJËÏOJ,Rª\x05\x00T\x83\x07b'
这现在是一个Unicode字符串,带有代码点,但我们仍然需要一个字节字符串。再次使用拉丁语1编码:

一步:

>>> 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 stackoverflow]$ 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十六进制。。。如果你看这里,你可以看到它是一个zlip

所以,我用zlib库来解码它。。。整洁的东西。

发生的事情是: 黑盒服务器正在发送字节之前对字节进行字符串化。您需要获取表示字节的字符串并将其转换回字节。最简单的方法是抽象语法树库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 stackoverflow]$ 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十六进制。。。如果你看这里,你可以看到它是一个zlip

所以,我用zlib库来解码它。。。整洁的东西。

不是你的吗
他提到的字符串的初始创建不正确?写s=\xac时,实际上是将第172个字符放在一个字符串中,而不是一个包含\xac的字符串中。@varlogtim OP不清楚,因此“假设”,但我看到了您的观点,查看OP的字节输出,并更新了我的答案以使用原始字符串。这是一个切分答案。我从来没有想过使用拉丁语-1,但你说得很好,它总是转换str->bytes和bytes->str,因为每个字符只有一个字节宽。你最初创建的字符串不是他提到的不正确吗?写s=\xac时,实际上是将第172个字符放在一个字符串中,而不是一个包含\xac的字符串中。@varlogtim OP不清楚,因此“假设”,但我看到了您的观点,查看OP的字节输出,并更新了我的答案以使用原始字符串。这是一个切分答案。我从来没有真正想过使用拉丁语-1,但你说得很好,它总是转换str->bytes和bytes->str,因为每个字符只有一个字节宽。仅供参考,使用AST解析器的速度较慢。对于这样一个小的字符串可能并不重要,但取决于OP的用例。我使用的编码/解码/编码方法似乎在所有转换过程中都会变慢,但timeit的速度比使用OP数据的ast.literal_eval 1.68us和7.98us快4.75倍。这不是编码挑战,实际上是我日常工作中遇到的一部分,我试图存储压缩的JSON,因为我们的空间非常有限。在zlib上的好捕捉;啊,是的,很有趣。我已经有一段时间没有考虑过这个问题了,但是如果你想用JSON存储压缩文件,我相信最好的方法是data->compression->base64->JSON。像你现在这样每字节使用4个字符并不是最好的。。。对于base64,随着字符串变长,编码应该比只存储十六进制字符(本质上是base16编码)要小。好的调用,我们将记住这一点。谢谢仅供参考,使用AST解析器的速度较慢。对于这样一个小的字符串可能并不重要,但取决于OP的用例。我使用的编码/解码/编码方法似乎在所有转换过程中都会变慢,但timeit的速度比使用OP数据的ast.literal_eval 1.68us和7.98us快4.75倍。这不是编码挑战,实际上是我日常工作中遇到的一部分,我试图存储压缩的JSON,因为我们的空间非常有限。在zlib上的好捕捉;啊,是的,很有趣。我已经有一段时间没有考虑过这个问题了,但是如果你想用JSON存储压缩文件,我相信最好的方法是data->compression->base64->JSON。像你现在这样每字节使用4个字符并不是最好的。。。对于base64,随着字符串变长,编码应该比只存储十六进制字符(本质上是base16编码)要小。好的调用,我们将记住这一点。谢谢