在二进制模式和文本模式下写入文件的区别。

65

在以文本模式打开的文件中写入数据时,与二进制模式下不同的翻译是什么?具体而言,在MS Visual C中。

unsigned char buffer[256];
for (int i = 0; i < 256; i++) buffer[i]=i;
int size  = 1;
int count = 256;

二进制模式:

FILE *fp_binary = fopen(filename, "wb");
fwrite(buffer, size, count, fp_binary);

与文本模式相比:

FILE *fp_text = fopen(filename, "wt");
fwrite(buffer, size, count, fp_text);

提出这个问题的动机是我想要在文本模式下不经意地写入磁盘的二进制数据。 - jholl
密切相关:https://dev59.com/12ct5IYBdhLWcg3wk-Vq - legends2k
7个回答

68
我相信大多数平台在处理流时会忽略“t”选项或“文本模式”选项。然而,在Windows上,情况并非如此。如果您查看fopen()函数的描述,链接为:MSDN,您将看到指定“t”选项将产生以下效果:
  • 输出时换行符('\n')将被转换为回车换行符('\r\n')序列。
  • 输入时回车换行符序列将被转换为换行符。
  • 如果以追加模式打开文件,则会检查文件结尾是否有ctrl-z字符(字符26),如果可能,将删除该字符。它还会将该字符的存在解释为文件结束。这是从CPM时代遗留下来的不幸遗物(关于父母的罪孽会传给他们的子孙三四代)。与先前的说法相反,不会附加ctrl-z字符。

14
回车符实际上是 '\r',换行符是 '\n'。 - Christoffer Hammarström
它是否对所有类型的文件操作都有这种行为?例如,fread和fwrite(主要用于二进制文件)? - Calmarius
当文件句柄被打开时,翻译是在低级别上指定的。无论您使用哪些函数来读取(或写入)文件,翻译都将发生。 - Jon Trauntvein
@Virus721:哦,谢谢,他两天后修好了,我没注意到。(虽然我不理解你的评论,但它确实引导了我的注意力。) - Cheers and hth. - Alf
1
据我回忆,CR和LF代码可以追溯到电传打字机的时代。发送回车符("\r")代码将使机器将其打印头返回到该行的起始位置,而换行符("\n")则会向前推进卷轴一行。即使在终端仿真器中,这些概念也被延续,即使它们在物理上已经失去了意义。 - Jon Trauntvein
显示剩余3条评论

32
在文本模式下,换行符"\n"可能会被转换为回车符和换行符"\r\n"。
通常情况下,您会想要以二进制模式打开。在文本模式下尝试读取任何二进制数据都无法正常工作,它会变得损坏。但是您可以在二进制模式下正确读取文本——只是不会自动将"\n"翻译为"\r\n"。
请参见fopen

4
阅读时,翻译的作用和你所描述的相反 - 将 "\r\n" 转换为 "\n"。 - Mark Ransom
2
techtonik:所有平台都允许您指定文本模式,但在Unix / Linux上与二进制模式没有区别。只有在Windows上才有所不同。(可能还有一些更晦涩的平台-您必须检查平台文档以确保) - Zebra North

6
此外,当您使用“rt”打开文件时,输入将以Ctrl-Z字符终止。

4
真实的情况是,我会自己创建文件格式,以类似于“my-file-type^Z”的方式开头,然后如果你从命令行键入/复制它,它只会给出文件的“魔数”,而不是向终端输出二进制数据。 - Zebra North

5
另一个区别是在使用 fseek 时。

如果流以二进制模式打开,则新位置正好是从文件开头开始测量的偏移量字节,如果 origin 是 SEEK_SET,则从当前文件位置开始测量,如果 origin 是 SEEK_CUR,则从文件结尾开始测量。如果 origin 是 SEEK_END,则某些二进制流可能不支持 SEEK_END。

如果流以文本模式打开,则 offset 的唯一支持值为零(适用于任何 origin),以及与同一文件关联的流上先前调用 std::ftell 返回的值(仅适用于 SEEK_SET 的 origin)。


5
尽管这个问题已经得到了回答并且被清晰地解释了,但我认为展示一个简单的代码例子来说明主要问题(\n和\r\n之间的翻译)是很有趣的。请注意,我没有涉及文件末尾的Crtl-Z字符的问题。
#include <stdio.h>
#include <string.h>

int main() {
    FILE *f;
    char string[] = "A\nB";
    int len;
    
    len = strlen(string);
    printf("As you'd expect string has %d characters... ", len); /* prints 3*/
    f = fopen("test.txt", "w"); /* Text mode */
    fwrite(string, 1, len, f);  /* On windows "A\r\nB" is writen */
    printf ("but %ld bytes were writen to file", ftell(f)); /* prints 4 on Windows, 3 on Linux*/ 
    fclose(f);
    return 0;
}

如果您在 Windows 上执行该程序,则会看到打印以下消息:
As you'd expect string has 3 characters... but 4 bytes were writen to file

当然,您也可以使用文本编辑器(如Notepad ++)打开文件,并自行查看字符:

enter image description here

在文本模式下读取文件时,Windows会执行反向转换。

4
我们在以文本模式打开文件时遇到一个有趣的问题,这些文件包含了混合换行符。
1\n\r
2\n\r
3\n
4\n\r
5\n\r

我们的要求是可以在文件中存储当前位置(使用了fgetpos),关闭文件,然后稍后重新打开文件并将其定位到该位置(使用了fsetpos)。
然而,如果文件包含混合行尾的内容,则此过程无法定位到实际相同的位置。在我们的情况下(我们的工具解析C ++),我们正在重新读取已经看过的文件部分。
使用二进制格式-这样您可以精确控制从文件中读取和写入的内容。

0
在'w'模式下,文件以写入模式打开,基本编码为'utf-8' 在'wb'模式下,文件以写入二进制模式打开,负责写入其他特殊字符,编码可能为'utf-16le'或其他编码。

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