【问题标题】:Base64 encoding issue in PythonPython中的Base64编码问题
【发布时间】:2018-09-10 09:54:04
【问题描述】:

我需要在 python 中保存一个 params 文件,这个 params 文件包含一些我不会保留在纯文本中的参数,所以我将整个文件编码为 base64(我知道这不是最安全的编码世界,但它适用于我需要使用的那种数据)。

使用编码,一切正常。我对文件的内容(一个带有适当扩展名的简单 txt)进行编码并保存文件。问题与解码有关。我在保存文件之前打印了编码的文本和从保存的文件中编码的文本,并且完全相同,但是由于我不知道的原因,保存的文件文本的解码返回了这个错误UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8d in position 1: invalid start byte并且在保存文件之前对文本的解码效果很好。

有解决这个问题的办法吗?

这是我的代码,我已尝试将所有内容转换为字节、字符串和所有内容......

params = open('params.bpr','r').read()


paramsencoded = base64.b64encode(bytes(params,'utf-8'))

print(paramsencoded)

paramsdecoded = str(base64.b64decode(str(paramsencoded,'utf-8')),'utf-8')

newparams = open('paramsencoded.bpr','w+',encoding='utf-8')
newparams.write(str(paramsencoded))
newparams.close()

params2 = open('paramsencoded.bpr',encoding='utf-8').read()
print(params2)

paramsdecoded = str(base64.b64decode(str(paramsencoded,'utf-8')),'utf-8')

paramsdecoded = base64.b64decode(str(params2))

print(str(paramsdecoded,'utf-8'))

【问题讨论】:

  • 您不需要在每次解码 Base64 数据时都将 paramsencoded 字节值解码为字符串。 b64decode() 也接受字节。
  • 而不是作为文本读取(使用'r')然后编码为字节,为什么不将文件读取为二进制(使用'rb'作为模式)?
  • 我不知道您为什么将paramsencoded.bpr 的文件打开为'w+'?你只需要写,不需要读,所以+可以被删除。同样的说明:以二进制模式打开并直接将bytes值写入str,而无需先解码。
  • 最后但并非最不重要的一点,您能否给我们一个重现问题的示例params 值?
  • 当您从open('paramsencoded.bpr',encoding='utf-8').read() 调用中读取文本时,无需使用str(params2)。最后一行的UnicodeDecodeError 到底在哪里抛出?

标签: python encoding base64 decoding


【解决方案1】:

你的错误在于你对base64.b64encode()返回的bytes对象的处理,你在对象上调用了str()

newparams.write(str(paramsencoded))

不解码bytes 对象:

>>> bytesvalue = b'abc='
>>> str(bytesvalue)
"b'abc='"

注意b'...' 符号。您生成了字节对象的 representation,它是一个包含 Python 语法的字符串,可以重现该值以进行调试(您可以复制该字符串值并将其粘贴到 Python 中以重新创建相同的 @ 987654328@值)。

这可能一开始并不容易注意到,因为base64.b64encode() 否则只会产生带有可打印 ASCII 字节的输出。

但是您的解码问题源于那里,因为在解码从文件读回的值时,开始时包含b' 字符。前两个字符被解释为 Base64 数据toob 是一个有效的 Base64 字符,' 被解析器忽略:

>>> bytesvalue = b'hello world'
>>> base64.b64encode(bytesvalue)
b'aGVsbG8gd29ybGQ='
>>> str(base64.b64encode(bytesvalue))
"b'aGVsbG8gd29ybGQ='"
>>> base64.b64decode(str(base64.b64encode(bytesvalue)))  # with str()
b'm\xa1\x95\xb1\xb1\xbc\x81\xdd\xbd\xc9\xb1\x90'
>>> base64.b64decode(base64.b64encode(bytesvalue))       # without str()
b'hello world'

注意输出是如何完全不同,因为 Base64 解码现在从错误的位置开始,因为 b 是第一个字节的前 6 位(使第一个解码字节为6C、6D、6E 或 6F 字节,因此 mnop ASCII)。

您可以正确解码该值(使用paramsencoded.decode('ascii')str(paramsencoded, 'ascii')),但您不应将任何此类数据视为文本。

改为以二进制模式打开您的文件。然后对bytes对象进行读写操作,base64.b64encode()base64.b64decode()函数也对bytes进行操作,完美匹配:

with open('params.bpr', 'rb') as params_source:
    params = params_source.read()  # bytes object

params_encoded = base64.b64encode(params)
print(params_encoded.decode('ascii'))   # base64 data is always ASCII data

params_decoded = base64.b64decode(params_encoded)

with open('paramsencoded.bpr', 'wb') as new_params:
    newparams.write(params_encoded)  # write binary data

with open('paramsencoded.bpr', 'rb') as new_params:
    params_written = new_params.read()

print(params_written.decode('ascii'))  # still Base64 data, so decode as ASCII

params_decoded = base64.b64decode(params_written)  # decode the bytes value

print(params_decoded.decode('utf8'))  # assuming the original source was UTF-8

我明确使用bytes.decode(codec) 而不是str(..., codec) 以避免意外的str(...) 调用。

【讨论】:

  • 是的!就是这样,我使用这种代码的语法很糟糕,使用decode('ascii) 并使用wbrb 像字节一样处理文件效果很好。非常感谢您的帮助!
猜你喜欢
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-21
相关资源
最近更新 更多