关于二进制文件的一般问题
我还是个初学者,对二进制文件有点困惑。当我在Python中以二进制模式写文件时,我写的其实只是普通文本,感觉没有什么二进制的东西。我知道我电脑上的每个文件都是二进制文件,但我还是搞不清楚我自己用二进制模式写的文件和那些像音频、视频等文件有什么区别,后者在文本编辑器里打开时会显示一堆乱码。
那些显示乱码的文件是怎么创建的呢?能不能给我一个小文件的例子,最好是用Python写的?
我觉得我问的问题可能很傻,但我真的很想知道。网上查了也没找到答案。
6 个回答
所谓的“文本”文件其实就是遵循某些规则的文件:这些文件里的字节通常是所有可能字节的一部分,通常是ASCII或Unicode值,并且这些字节会被组织成“行”,每行的结束有“行结束符”。不同的平台有不同的标准行结束符——Unix用的是\n
,Mac用的是\r
,而Windows用的是\r\n
。所以,处理这些文件时需要根据情况进行转换。对于文本文件来说,这样处理是没问题的,但如果是其他类型的文件,比如音频文件,里面的0x0a
(\n
)字节就不适合被转换成0x0d
0x0a
(\r\n
)。当然,如果你一直在用Unix系统,这个问题可能就不会出现。
在Python 3中,所有字符串都是Unicode格式,打开一个文件作为文本时,你需要读写Unicode字符串,并且可能需要指定编码(默认是UTF-8)。而如果你打开一个文件作为二进制文件,就需要使用bytes
对象,这些对象就是简单的8位字节列表,不会被编码。
这样解释清楚了吗?
当我以二进制模式写入文件时(在Python中),我只是写普通的文本。
当你升级到Python 3.x时,你需要改变一下写法:
>>> f = open(filename, 'wb')
>>> f.write("Hello, world!\n")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: must be bytes or buffer, not str
>>> f.write(b"Hello, world!\n")
14
不过你的问题其实不是关于二进制文件,而是关于 str
的。
在Python 2.x中,str
是一个字节序列,它有两种含义:
- 一种是非Unicode字符串,或者
- 原始的二进制数据(比如图像中的像素)。
如果你把后者当成前者来打印,就会得到一堆乱码。
Python 3.x 通过引入一个单独的 bytes
类型来处理二进制数据,解决了这种双重含义的问题,使得 str
明确表示为文本字符串(并且变成了Unicode)。
这是对你问题的直接回答:
import struct
with open('gibberish.bin', 'wb') as f:
f.write(struct.pack('<4d', 3.14159, 42.0, 123.456, 987.654))
这段代码是把四个浮点数打包成一种二进制格式(小端格式的IEEE 756 64位浮点数)。
以下是你需要了解的一些内容:
以二进制模式读取和写入文件时,数据不会发生任何变化。相比之下,文本模式下,读取或写入的数据会根据平台的“文本文件”约定进行转换,包括任何Unicode的编码和解码。
在Unix/Linux/Mac OS X系统中:数据没有变化
在旧版Mac中:行分隔符是\r
,会被转换为Python标准的\n
在Windows系统中:行分隔符是\r\n
,会被转换为\n
。还有一个不太为人知的事实是,Ctrl-Z也就是\x1a
被视为文件结束,这个约定源自CP/M
,它记录文件大小是用的128字节扇区的数量。