Windows上Python的二进制I/O与文本I/O的区别

27 投票
4 回答
8823 浏览
提问于 2025-04-16 01:21

我知道打开一个二进制文件时应该用 "rb",而不是 "r",因为在Windows系统中,二进制文件和非二进制文件的处理方式是不同的。

但是我不太明白如果我用错方式打开文件会发生什么,以及为什么要有这样的区分。其他操作系统似乎对这两种文件的处理都没问题。

4 个回答

1

在Windows系统中,文本模式会把换行符 \n 转换成回车符加换行符 \r\n

如果你以二进制模式读取文本,那就没有问题。如果你在文本模式下读取二进制数据,数据很可能会被搞坏。

28

这段话主要是讲为什么文件的打开模式会有这样的历史背景。文件的打开方式是从C语言的标准输入输出库继承来的,所以我们就沿用了这个方式。

在Windows系统中,文本文件和二进制文件没有区别,就像在任何Unix系统中一样。真的,我是说真的!在某些文件系统或操作系统中,文本文件和对象文件是完全不同的东西。在一些系统中,你还得提前指定行的最大长度,并使用固定大小的记录……这些都是从80列打孔卡片时代留下来的古老遗物。幸运的是,在Unix、Windows和Mac中并没有这样的情况。

不过,尽管其他条件相同,Unix、Windows和Mac在输出流中用来表示一行结束的字符(或者说是行与行之间的分隔符)上,历史上是有区别的。在Unix中,使用的是\x0A(也就是\n)。在Windows中,使用的是两个字符的组合\x0D\x0A(也就是\r\n);而在Mac中,则只用\x0D(也就是\r)。这两个符号的来源有一些小故事——ASCII码10被称为换行符(Line Feed, LF),当它发送到电传打字机时,会让它向下移动一行(Y++),而不改变它的水平位置(X)。而回车符(Carriage Return, CR),ASCII码13,则会让打印机的打印头返回到行的开头(X=0),而不向下滚动一行。所以在发送输出到打印机时,必须同时发送\r和\n,这样打印头才能移动到新行的开头。在终端键盘上输入时,操作员自然希望只按一个键而不是两个来结束一行。在Apple][上,这个键就是'Return'(\r)。

总之,这就是事情的发展过程。C语言的创造者们很关心可移植性——因为大部分Unix都是用C写的,而以前的操作系统都是用汇编语言写的。所以他们不想处理每个平台在文本表示上的特殊情况,于是他们在输入输出库中添加了这个“黑科技”,根据平台的不同,输入和输出的文件会在后台“修补”,让程序看到的新行都是正宗的Unix方式——即'\n',无论它原本是来自Windows的'\r\n'还是Mac的'\r'。这样,开发者就不需要担心程序运行在哪个操作系统上,它仍然可以以本地格式读取和写入文本文件。

不过,问题来了——并不是所有文件都是文本文件,还有其他格式的文件,它们对字符的替换非常敏感。所以他们想,我们就把这些文件称为“二进制文件”,并通过在fopen()中加入'b'来表示这一点——这样就可以告诉库不要进行任何后台转换。这就是事情发展成现在这样的原因 :)

总结一下,如果文件是以'b'模式打开的二进制文件,就不会进行任何转换。如果是以文本模式打开的,根据平台的不同,可能会进行一些换行字符的转换——朝着Unix的标准去处理。当然,在Unix平台上,读取或写入“文本”文件和“二进制”文件是没有区别的。

26

这个模式是关于行结束符的转换。

在文本模式下读取文件时,系统默认的行结束符(在Windows上是\r\n)会被转换成Python使用的Unix风格的\n行结束符。而在文本模式下写入文件时,情况正好相反。

在二进制模式下,则不会进行这样的转换。

其他平台通常不需要这种转换,因为它们默认使用\n来表示行结束符。(不过,Mac OS以前是用\r的,这算是个例外。)依赖这种转换的代码在不同平台上可能无法正常工作。

撰写回答