Python中的Base64编码问题

3
我需要在Python中保存一个参数文件,此参数文件包含一些参数,我不想以明文形式留下它们,因此我将整个文件编码为base64(我知道这不是世界上最安全的编码方式,但它适用于我需要使用的数据类型)。
使用编码时,一切都很顺利。我对文件的内容进行编码(一个简单的带有适当扩展名的文本文件),并保存该文件。问题出现在解码时。我打印了保存文件之前已编码的文本和保存后的编码文本,它们完全相同,但由于某种原因,保存文件的文本解码会返回以下错误 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'))

1
你不需要每次想要解码Base64数据时都将paramsencoded字节值解码为字符串。b64decode()也接受字节作为参数。 - Martijn Pieters
1
而不是将文本读取为文本(使用“'r'”),然后编码为字节,为什么不将文件作为二进制文件读取(使用“'rb'”作为模式)? - Martijn Pieters
1
我不确定您为什么使用'w+'打开paramsencoded.bpr文件?您只需要写入,而不需要读取,因此可以去掉+。同样的备注:以二进制模式打开并直接将bytes值写入,而无需先解码为str - Martijn Pieters
1
最后但并非不重要的是,您能否给我们提供一个样本“params”值以重现问题? - Martijn Pieters
1
当您仅从open('paramsencoded.bpr',encoding='utf-8').read()调用中读取文本时,无需使用str(params2)UnicodeDecodeError在哪一行抛出的? - Martijn Pieters
1个回答

4

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

newparams.write(str(paramsencoded))

这样并不能解码bytes对象:

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

请注意b'...'的表示方法。你生成了字节对象的表示,它是一个包含Python语法的字符串,可以用于调试目的(你可以复制该字符串值并将其粘贴到Python中以重新创建相同的bytes值)。
这可能一开始不太容易注意到,因为base64.b64encode()通常只会产生可打印的ASCII字节输出。
但是你的解码问题源于此,因为从文件读回的值在开头包括b'字符。这前两个字符也被解释为Base64数据;b是一个有效的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打开文件并将其视为字节非常有效。非常感谢您的帮助! - David González Blazman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接