打开文件时以二进制方式和文本方式有什么区别?

9

我做过一些类似于以下的事情:

FILE* a = fopen("a.txt", "w");
const char* data = "abc123";
fwrite(data, 6, 1, a);
fclose(a);

在生成的文本文件中,它会显示“abc123”,就像预期的那样。但是接下来我做了以下操作:
//this time it is "wb" not just "w"
FILE* a = fopen("a.txt", "wb");
const char* data = "abc123";
fwrite(data, 6, 1, a);
fclose(a);

并且获得完全相同的结果。如果我使用二进制或正常模式读取文件,也会给我相同的结果。所以我的问题是,在使用或不使用二进制模式下打开文件之间有什么区别。

我在哪里阅读有关fopen模式的信息:http://www.cplusplus.com/reference/cstdio/fopen/


1
尝试在Windows上以文本模式打开文件并写入一堆换行符,然后尝试读取它。 - user529758
是的,我现在看到了重复部分。之前没有注意到,非常抱歉。 - BWG
顺便提一下:我注意到你标记了这个问题为C++,而不是C。我可以建议使用std::ifstreamstd::ofstream代替C库吗?这更符合惯用法,并确保您无需担心是否需要调用close,无论是否抛出异常(除了提供其他好的抽象)。 - Mark
@Mark 我会调查一下。 - BWG
请注意,链接到重复问题的所选择的解决方案在细节上是不正确的,当我写这篇文章时。 - Cheers and hth. - Alf
2个回答

13
您提供的链接实际上描述了差异,但它被埋在页面底部:

http://www.cplusplus.com/reference/cstdio/fopen/

文本文件是包含文本行序列的文件。根据应用程序运行的环境,输入/输出操作中可能会发生一些特殊字符转换以适应特定于系统的文本文件格式。尽管在某些环境中不进行任何转换,文本文件和二进制文件被同样对待,但使用适当的模式可以提高可移植性。这种转换可能是将\r\n规范化为\n(反之亦然),或者忽略超过0x7F的字符(类似FTP中的“文本模式”)。个人建议以二进制模式打开所有内容,并使用良好的Unicode或其他文本编码库来处理文本。

我本来也不打算处理文本,所以这很好。但由于某种原因,我要在10分钟后才能接受。 - BWG

8
需要翻译的内容如下:

需要注意的最重要的区别是,在文本模式下打开的流会在非*nix系统上进行换行符转换(也用于网络通信,但标准库不支持)。在*nix中,换行符只是ASCII换行符\n,对于文本的内部和外部表示都是如此。在Windows中,外部表示通常使用回车+换行符对,“CRLF”(ASCII代码13和10),它被转换为单个\n输入,反之亦然。


来自C99标准(N869草案文件)的§7.19.2/2,

文本流是由字符组成的有序序列,组成每个行的是零个或多个字符以及终止的换行符字符。最后一行是否需要一个终止的换行符字符是实现定义的。可能需要添加、更改或删除输入和输出的字符以符合主机环境中不同的文本表示约定。因此,在流中的字符与外部表示中的字符之间不必存在一一对应关系。从文本流中读取的数据只有在以下情况下才会与先前写入该流的数据相等:数据仅包含打印字符和控制字符水平制表符和换行符;没有空格字符紧接着换行符字符;最后一个字符是换行符字符。在换行符字符之前立即写出的空格字符是否在读入时出现是实现定义的。

在§7.19.3/2中:

二进制文件不会被截断,除非在7.19.5.3中定义。文本流上的写操作是否导致相关文件被截断超过该点是实现定义的。

关于使用fseek,在§7.19.9.2/4中:

对于文本流,要么offset为零,要么offset为与同一文件相关联的流上的ftell函数的早期成功调用返回的值,并且whence应为SEEK_SET

关于使用ftell,在§17.19.9.4中:

ftell函数获取指向stream的文件位置指示器的当前值。对于二进制流,该值是从文件开头算起的字符数。对于文本流,其文件位置指示器包含未指定的信息,可由fseek函数使用,以将流的文件位置指示器返回到ftell调用时的位置;两个这样的返回值之间的差异不一定是读取或写入的字符数的有意义度量。

我认为这是最重要的内容,但还有一些细节。


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