【问题标题】:How do I write raw binary data in Python?如何在 Python 中编写原始二进制数据?
【发布时间】:2010-04-09 21:58:06
【问题描述】:

我有一个 Python 程序,用于存储数据并将数据写入文件。数据是原始二进制数据,内部存储为str。我正在通过 utf-8 编解码器写出来。但是,我在cp1252.py 文件中得到了UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 25: character maps to <undefined>

在我看来,这就像 Python 正在尝试使用默认代码页解释数据。但它没有默认代码页。这就是为什么我使用str,而不是unicode

我想我的问题是:

  • 如何在 Python 中表示内存中的原始二进制数据?
  • 当我通过编解码器写入原始二进制数据时,如何对其进行编码/取消编码?

【问题讨论】:

  • 你说你在str中有原始数据,你是如何生成这些数据的?我假设你在某个地方有一个 unicode 源,但我不清楚你是在 str 中写入“原始”unicode,还是从文件中读取它(类似对象)或...... (发布一个演示此错误的示例字符串会很有帮助!)

标签: python codec string


【解决方案1】:

注意:这是为 Python 2.x 编写的。不确定是否适用于 3.x。

您对内存中的原始二进制数据使用str 是正确的。
[如果您使用的是 Python 2.6+,最好使用 bytes,它在 2.6+ 中只是 str 的别名,但可以更好地表达您的意图,如果有一天您将代码移植到 Python 3,将会有所帮助。 ]

正如其他人所说,通过编解码器写入二进制数据很奇怪。写入编解码器采用 unicode 并将字节输出 到文件中。你试图倒退,因此我们对你的意图感到困惑......

[并且您对错误的诊断看起来是正确的:由于编解码器需要 unicode,Python 正在使用系统的默认编码将您的 str 解码为 un​​icode,这令人窒息。]

您希望在输出文件中看到什么?

  • 如果文件应按原样包含二进制数据

    那么你不能通过编解码器发送它;你必须写 直接进入文件。编解码器对一切进行编码,并且只能 发出有效的 unicode 编码(在您的情况下为有效的 UTF-8)。 没有输入可以让它发出任意 字节序列!

    • 如果您需要 UTF-8 和原始二进制数据的混合,您 应该直接打开文件,并混合写入some_datasome_text.encode('utf8')...

    但是请注意,将 UTF-8 与原始任意数据混合是非常 糟糕的设计,因为这样的文件处理起来很不方便 和!理解 unicode 的工具会卡在二进制文件上 数据,让您甚至无法方便地查看(更不用说 修改)文件。

  • 如果你想友好地表示任意字节 统一码

    data.encode('base64') 传递给编解码器。 Base64 只产生 干净的 ascii(字母、数字和一些标点符号)所以它 可以清楚地嵌入到任何东西中,它在人们看来显然是 二进制数据,并且相当紧凑(略超过 33% 开销)。

    附:你可能会注意到data.encode('base64') 很奇怪。

    • .encode() 应该采用 unicode 但我给它一个 细绳?! Python 有几个伪编解码器可以转换 str->str 比如'base64'和'zlib'。

    • .encode() 总是返回一个 str 但你会将它输入到编解码器中 期待unicode?!在这种情况下,它将只包含干净 ascii,所以没关系。你可以明确写 data.encode('base64').encode('utf8') 如果它让你觉得 更好。

  • 如果您需要从任意字节到 unicode 的 1:1 映射

    data.decode('latin1') 传递给编解码器。 latin1地图 字节 0-255 到 unicode 字符 0-255,这有点优雅。

    编解码器当然会对您的字符进行编码 - 128-255 是 以 UTF-8 编码为 2 或 3 个字节(令人惊讶的是,平均 开销是 50%,超过 base64!)。这相当杀死 1:1 映射的“优雅”。

    另请注意,Unicode 字符 0-255 包括 nasty 不可见/控制字符(换行符、换页符、软连字符等) 让您的二进制数据在文本编辑器中查看起来很烦人。

    考虑到这些缺点,我不推荐 latin1,除非 你明白你为什么想要它。
    我只是提到它是另一种出现的“自然”编码 记住。

【讨论】:

    【解决方案2】:

    对于您的第一个问题:在 Python 中,常规字符串(即,不是 unicode 字符串)是二进制数据。如果要写unicode字符串和二进制数据,把unicode字符串转成二进制数据放在一起:

    # encode the unicode string as a string
    bytes = unicodeString.encode('utf-8')
    # add it to the other string
    raw_data += bytes
    # write it all to a file
    yourFile.write(raw_data)
    

    关于你的第二个问题:你write()原始数据;然后,当您阅读它时,您会这样做:

    import codecs
    yourFile = codecs.open( "yourFileName", "r", "utf-8" )
    # and now just use yourFile.read() to read it
    

    【讨论】:

    • 正如我提到的,我一个常规字符串。
    • 而做yourFile.write(regular_string) 会给你错误?您不需要进一步编码常规字符串;就像我说的,它已经是原始字节了。
    • @Chris:你是不是在做一些愚蠢的事情,比如使用 Python 3?
    • 这不是 Python 3。它是一个 str,通过 utf-8 编解码器编写,在此过程中不知何故被 cp1252 编解码器解释。我怀疑 Python 的编解码器需要 unicode 字符串,因此它会自动将 str 转换为 unicode 对象,这会导致转换和错误。不过,我不太清楚如何防止这种情况发生。
    • 如果您将原始二进制数据存储在 str 中,您不希望在编解码器附近获取它。它应该直接写入以二进制模式打开的文件。我不知道你说你在内部以 utf-8 格式存储原始二进制数据是什么意思。这没有意义。
    【解决方案3】:

    您通常不应将编解码器与str 一起使用,除非将它们转换为unicodes。如果您认为自己想要 unicode 中的“原始”数据,也许您应该考虑使用 latin-1 编解码器。

    【讨论】:

    • 我正在将原始二进制数据以及一些 unicode 字符串写入文本文件。当我尝试将原始二进制数据(我内部以 utf-8 格式存储)写入 utf-8 编解码器时,出现 cp1252 错误。
    • 然后直接写入文件,而不是通过编解码器。
    猜你喜欢
    • 1970-01-01
    • 2012-11-30
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-15
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    相关资源
    最近更新 更多