如何有效地将二进制数据存储在 CSV 等文本格式的文件中?
How can I effectively store binary data in a file that's in a text format like CSV?
我目前正在 Python 中开发密码存储程序,尽管 C 可能会更快。在过去的一个小时左右的时间里,我一直在尝试寻找一种将字节对象存储在 CSV 文件中的方法。我用他们自己的盐对密码进行哈希处理,然后存储它,然后再次抓取它来检查密码。当它存储在内存中时,它工作得很好。
salt = os.urandom(64)
hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
1000000
)
storage = salt + hash
salt_from_store = storage[:64]
hash_from_store = storage[64:]
但是,当我尝试将它存储在 CSV 文件中时,它不必一直 运行,我收到错误消息,
TypeError: write() argument must be str, not bytes
所以,我使用
将其转换为字符串
str(storage)
写得很好。但是,当我从文件中获取它时,它仍然是一个字符串,长度从 128(字节)到 300+(字符)。它也从不一致。我不知道编码,所以我不能那样改变它,当我打印字节时,它是一堆带有反斜杠和 X 的字符
b'\xfd\x3a'
偶尔会出现一些随机的特殊字符。我不确定是否有办法将其转换为 int,然后再将其转换回来。另一个问题是我找到了一种方法,通过更改
b"\xf1\x96"
到
"b\xf1\x96"
它打印编码文本,而不是它所组成的字节。但是,我不知道这是否是一种改变它的好方法,如果是,是否有一种方法可以在没有类似
的情况下做到这一点
bytes[0] = '"'
bytes[1] = 'b'
要写入字节,要么写入预期包含字节的内容,要么写入以某种方式表示字节的文本。 CSV 基本上是一种基于文本的格式。如果您要使用 CSV 文件,那么您将以文本模式打开它,然后向其中写入文本。
从根本上说,硬盘上的每个文件都是由字节组成的。这意味着,当您打开 CSV 文件时,您将选择(或使用默认)文本编码方案。因此,您的 bytes
对象必须在写入时转换两次(转换为文本,然后转换为文件中的底层字节 - 例如,您可以使用十六进制编辑器验证),并在读取时再次转换两次。这就是处理混合数据的现实。值得庆幸的是,一半的工作会自动为您处理(通过 open
调用,或像 csv.Reader
这样的包装器)。
So, I converted it to a string using str(storage)
从您最有可能感兴趣的意义上说,这实际上不是转换。这是要求可打印的、人类可读的表示对象(有还有 repr
,它要求更面向技术的表示。对于 str
和 bytes
对象,这就是封闭引号的来源,以及其他调整。当你 print
something, its str
is used. 当您在 REPL 中评估某些内容时,您会看到结果的 repr
- 除了当结果为 None
时,它不会显示任何内容根本)。专门针对bytes
和str
对象的处理,Python有一个encoding和decoding的概念,它使用明确的 .encode
(str
->bytes
) 和 .decode
(bytes
->str
) 方法。这些是您可以在文档中轻松查找的主题(或以前的 Stack Overflow 问题,或者通常在 Internet 上)。
when I print the bytes, it's a bunch of characters with backslashes and X's
是的,这是 Python 用来告诉您 bytes
对象中存在哪些数据的形式。你在这里说的基本上与“当我打印列表时,它是一堆列表元素,逗号被方括号括起来”,或者“当我打印整数时,它是一堆数字符号”。
But then, when I get it from the file, it's still a string, and the length goes from 128 (bytes) to 300+ (chars).
所以再次解码。当然,您确实需要正确编码。您从文件中获得的所有内容都将是一个字符串,因为您正在以文本模式打开文件,因为 CSV 是一种文本格式。 (顺便说一句,您 正在 使用 csv
标准库模块,对吧?)
It's also never consistent. I don't know the encoding
所以告诉它使用哪种编码;如果您需要使用一致数量的文本,请选择一种将一个字节一致地映射到一个 Unicode 代码点的编码(例如 latin-1
,也称为 iso-8859-1
)。但我怀疑您实际上并不关心文本的长度(如果有的话,您会关心文件中使用的字节数)。
I've found a way to do it, by changing
您只能对文字数据执行此操作。不要用这些术语来思考。 b
是 语言语法 的一部分。它是不是数据。
你可以使用十六进制。让我们获取一些数据:
>>> import os
>>> b = os.urandom(10)
>>> b
b'\xc5\xe2{\xdf\xd2\x13\xa7\x0b\xef\x07'
作为可以写入 CSV 的十六进制字符串:
>>> b.hex()
'c5e27bdfd213a70bef07'
返回字节:
>>> bytes.fromhex(b.hex())
b'\xc5\xe2{\xdf\xd2\x13\xa7\x0b\xef\x07'
如果你想将字节保存为字符串,你应该将它们编码成一种格式,比如 base64. This is more efficient with space than directly writing hex.
尝试将任意字节直接转换为 utf-8 等编码可能会导致 UnicodeDecodeError
错误。
在你的情况下,你可以这样做:
import os, hashlib, base64
password = "top_secret"
salt = os.urandom(64)
hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
1000000
)
storage = salt + hash
# convert to a base64 string:
s = base64.b64encode(storage).decode('utf-8')
print(s) # <-- string you can save this to a file
# after reading it back from a file convert back to bytes
the_bytes = base64.b64decode(s)
the_bytes == storage
# True
我目前正在 Python 中开发密码存储程序,尽管 C 可能会更快。在过去的一个小时左右的时间里,我一直在尝试寻找一种将字节对象存储在 CSV 文件中的方法。我用他们自己的盐对密码进行哈希处理,然后存储它,然后再次抓取它来检查密码。当它存储在内存中时,它工作得很好。
salt = os.urandom(64)
hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
1000000
)
storage = salt + hash
salt_from_store = storage[:64]
hash_from_store = storage[64:]
但是,当我尝试将它存储在 CSV 文件中时,它不必一直 运行,我收到错误消息,
TypeError: write() argument must be str, not bytes
所以,我使用
将其转换为字符串str(storage)
写得很好。但是,当我从文件中获取它时,它仍然是一个字符串,长度从 128(字节)到 300+(字符)。它也从不一致。我不知道编码,所以我不能那样改变它,当我打印字节时,它是一堆带有反斜杠和 X 的字符
b'\xfd\x3a'
偶尔会出现一些随机的特殊字符。我不确定是否有办法将其转换为 int,然后再将其转换回来。另一个问题是我找到了一种方法,通过更改
b"\xf1\x96"
到
"b\xf1\x96"
它打印编码文本,而不是它所组成的字节。但是,我不知道这是否是一种改变它的好方法,如果是,是否有一种方法可以在没有类似
的情况下做到这一点bytes[0] = '"'
bytes[1] = 'b'
要写入字节,要么写入预期包含字节的内容,要么写入以某种方式表示字节的文本。 CSV 基本上是一种基于文本的格式。如果您要使用 CSV 文件,那么您将以文本模式打开它,然后向其中写入文本。
从根本上说,硬盘上的每个文件都是由字节组成的。这意味着,当您打开 CSV 文件时,您将选择(或使用默认)文本编码方案。因此,您的 bytes
对象必须在写入时转换两次(转换为文本,然后转换为文件中的底层字节 - 例如,您可以使用十六进制编辑器验证),并在读取时再次转换两次。这就是处理混合数据的现实。值得庆幸的是,一半的工作会自动为您处理(通过 open
调用,或像 csv.Reader
这样的包装器)。
So, I converted it to a string using
str(storage)
从您最有可能感兴趣的意义上说,这实际上不是转换。这是要求可打印的、人类可读的表示对象(有还有 repr
,它要求更面向技术的表示。对于 str
和 bytes
对象,这就是封闭引号的来源,以及其他调整。当你 print
something, its str
is used. 当您在 REPL 中评估某些内容时,您会看到结果的 repr
- 除了当结果为 None
时,它不会显示任何内容根本)。专门针对bytes
和str
对象的处理,Python有一个encoding和decoding的概念,它使用明确的 .encode
(str
->bytes
) 和 .decode
(bytes
->str
) 方法。这些是您可以在文档中轻松查找的主题(或以前的 Stack Overflow 问题,或者通常在 Internet 上)。
when I print the bytes, it's a bunch of characters with backslashes and X's
是的,这是 Python 用来告诉您 bytes
对象中存在哪些数据的形式。你在这里说的基本上与“当我打印列表时,它是一堆列表元素,逗号被方括号括起来”,或者“当我打印整数时,它是一堆数字符号”。
But then, when I get it from the file, it's still a string, and the length goes from 128 (bytes) to 300+ (chars).
所以再次解码。当然,您确实需要正确编码。您从文件中获得的所有内容都将是一个字符串,因为您正在以文本模式打开文件,因为 CSV 是一种文本格式。 (顺便说一句,您 正在 使用 csv
标准库模块,对吧?)
It's also never consistent. I don't know the encoding
所以告诉它使用哪种编码;如果您需要使用一致数量的文本,请选择一种将一个字节一致地映射到一个 Unicode 代码点的编码(例如 latin-1
,也称为 iso-8859-1
)。但我怀疑您实际上并不关心文本的长度(如果有的话,您会关心文件中使用的字节数)。
I've found a way to do it, by changing
您只能对文字数据执行此操作。不要用这些术语来思考。 b
是 语言语法 的一部分。它是不是数据。
你可以使用十六进制。让我们获取一些数据:
>>> import os
>>> b = os.urandom(10)
>>> b
b'\xc5\xe2{\xdf\xd2\x13\xa7\x0b\xef\x07'
作为可以写入 CSV 的十六进制字符串:
>>> b.hex()
'c5e27bdfd213a70bef07'
返回字节:
>>> bytes.fromhex(b.hex())
b'\xc5\xe2{\xdf\xd2\x13\xa7\x0b\xef\x07'
如果你想将字节保存为字符串,你应该将它们编码成一种格式,比如 base64. This is more efficient with space than directly writing hex.
尝试将任意字节直接转换为 utf-8 等编码可能会导致 UnicodeDecodeError
错误。
在你的情况下,你可以这样做:
import os, hashlib, base64
password = "top_secret"
salt = os.urandom(64)
hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
1000000
)
storage = salt + hash
# convert to a base64 string:
s = base64.b64encode(storage).decode('utf-8')
print(s) # <-- string you can save this to a file
# after reading it back from a file convert back to bytes
the_bytes = base64.b64decode(s)
the_bytes == storage
# True