【问题标题】:Convert binary data to web-safe text and back - Python将二进制数据转换为网络安全文本并返回 - Python
【发布时间】:2011-12-13 16:35:25
【问题描述】:

我想将二进制文件(例如 jpg、mp3 等)转换为网络安全文本,然后再转换回二进制数据。我研究了几个模块,我认为我真的很接近,但我不断收到数据损坏。

查看binascii 的文档后,我想到了这个:

from binascii import *
raw_bytes = open('test.jpg','rb').read()
text = b2a_qp(raw_bytes,quotetabs=True,header=False)
bytesback = a2b_qp(text,header=False)
f = open('converted.jpg','wb')
f.write(bytesback)
f.close()

当我尝试打开 converted.jpg 时,我得到数据损坏:-/

我还尝试将b2a_base64 与 57 长的二进制数据块一起使用。我将每个块转换为字符串,将它们连接在一起,然后再转换回a2b_base64 并再次损坏。

有人可以帮忙吗?我对字节和文件格式的所有复杂性并不是非常了解。如果这与 \r\n 的东西有区别,我会在 Windows 上使用 Python

【问题讨论】:

  • base64 似乎总是对我有用。也许先序列化/腌制数据?
  • 你必须有一个定制的 bassackwards 版本的 Windows -- 标准问题有 \r\n 的东西。
  • 不要在问题中添加答案(我已将您的答案从问题 here 中移出)。要将问题标记为“已解决”,只需接受最好的答案,包括您自己的答案。

标签: python file binary ascii utf


【解决方案1】:

您的代码看起来相当复杂。试试这个:

#!/usr/bin/env python

from binascii import *
raw_bytes = open('28.jpg','rb').read()
i = 0
str_one = b2a_base64(raw_bytes) # 1
str_list = b2a_base64(raw_bytes).split("\n") #2

bytesBackAll = a2b_base64(''.join(str_list)) #2
print bytesBackAll == raw_bytes #True #2

bytesBackAll = a2b_base64(str_one) #1
print bytesBackAll == raw_bytes #True #1

标有#1#2 的行代表彼此的替代品。 #1 对我来说似乎最直接 - 只需将其设为一个字符串,对其进行处理并将其转换回来。

【讨论】:

    【解决方案2】:

    您应该使用 base64 编码而不是引用的 printable。使用b2a_base64()a2b_base64()

    对于像图片这样的二进制数据,引用的可打印文件要大得多。在此编码中,每个二进制(非字母数字字符)代码都更改为=HEX。它可用于主要由字母数字组成的文本,如电子邮件主题。

    Base64 对于主要是二进制数据要好得多。它占用第一个字节的 6 位,然后是第一个字节的最后 2 位和从第二个字节开始的 4 位。等。可以通过编码文本末尾的=填充来识别(有时使用其他字符)。

    作为示例,我采用了 271 700 字节的 .jpeg。在 qp 中为 627 857 b,而在 base64 中为 362 269 字节。 qp 的大小取决于数据类型:只有字母的文本不会改变。 base64 的大小为orig_size * 8 / 6

    【讨论】:

    • 是的,工作,感谢您的帮助。它不早点工作的原因是我使用的是raw_bytes[0:56] 而不是[0:57]。我还使用 '+=' 附加字符串,而不是加入字符串列表,但我不确定这是否会有所不同。
    • FTR:需要 6、2+4、4+2、6 位。使用 7 位,它将是 Base128。
    【解决方案3】:

    您的文档参考适用于 Python 3.0.1。没有充分的理由使用 Python 3.0。您应该使用 3.2 或 2.7。你到底在用什么?

    建议:(1) 将 bytes 更改为 raw_bytes 以避免与内置的 bytes 混淆 (2) 在测试脚本中检查 raw_bytes == bytes_back (3) 在测试时应该与quoted-printable一起使用,对于二进制数据来说效率非常低;改用 base64。

    更新:Base64 编码为每 3 个输入字节生成 4 个输出字节。您的 base64 代码不适用于 56 字节块,因为 56 不是 3 的整数倍;每个块被填充为 3 的倍数。然后你加入块并尝试解码,这保证不会工作。

    你的分块循环最好写成:

    output_string = ''.join(
        b2a_base64(raw_bytes[i:i+57]) for i in xrange(0, xrange(len(raw_bytes), 57)
        )
    

    无论如何,分块是相当缓慢且毫无意义的;就做b2a_base64(raw_bytes)

    【讨论】:

    • 我使用的是 2.7,抱歉省略了。我检查了raw_bytes == bytes_back,并使用上面的脚本得到了False,所以我不确定这是否是Python比较字节字符串的问题,或者我在调用函数时没有指定正确的参数。
    • 如果我将它分成 [0:57] 而不是 [0:56] 的块,结果证明它适用于 base64;我现在将其写在问题中。再次感谢!
    【解决方案4】:

    @PMC 从问题中复制的答案:

    以下是有效的:

    from binascii import *
    raw_bytes = open('28.jpg','rb').read()
    str_list = []
    i = 0
    while i < len(raw_bytes):
        byteSegment = raw_bytes[i:i+57]
        str_list.append(b2a_base64(byteSegment))
        i += 57
    bytesBackAll = a2b_base64(''.join(str_list))
    print bytesBackAll == raw_bytes #True
    

    感谢各位的帮助。我不知道为什么这会以 [0:56] 而不是 [0:57] 失败,但我会把它留给读者作为练习:P

    【讨论】:

    • 这确实“有效”,但这是代码膨胀的一个极端例子——请参阅我更新的答案。
    • @John Machin:如果不清楚:这是 \@PMC 的答案。将您的评论转给他。
    • 这条评论是针对整个世界的。
    猜你喜欢
    • 2020-10-20
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    • 2017-02-05
    相关资源
    最近更新 更多