我正在运行以下代码:
#include<stdio.h>
#include<string.h>
#include<io.h>
int main(){
FILE *fp;
if((fp=fopen("test.txt","r"))==NULL){
printf("File can't be read\n");
exit(1);
}
char str[50];
fgets(str,50,fp);
printf("%s",str);
return 0;
}
text.txt包含:I am a boy\r\n
由于我在Windows上,它需要\ r \ n作为新行字符,所以如果我从文件中读取它,它应该"I am a boy\n\0"
存储str
,但我得到{{1 }}。我正在使用mingw编译器。
答案 0 :(得分:7)
行为取决于c库实现以及传递给fopen
的模式。请参阅fopen
(fopen on MSDN)上的MSDN文档中的此引用:
b - 以二进制(未翻译)模式打开;涉及回车符和换行符的翻译被抑制。
意味着,如果您使用Microsoft c库,并打开省略'b'的文件,则将从流中删除回车符。
由于你正在使用mingw,你的编译器可能会链接到遵循POSIX标准的GNU c库。这就是GNU文档中关于fopen
(fopen on gnu.org):
opentype中的字符'b'具有标准含义;它请求二进制流而不是文本流。但这对POSIX系统(包括GNU系统)没有任何影响。
结论:你省略了'b'模式char,它以文本模式打开你的流。你在Windows上,但使用GNU c库,它在文本和二进制模式之间没有区别。这就是fgets
同时读取回车符和新行的原因。
答案 1 :(得分:5)
由于我在Windows上,它需要\ r \ n作为换行符...
这个假设是错误的。 C标准将回车和新线视为两种不同的东西,如C99§5.2.1/ 3(字符集)所示:
[...]在基本执行字符集中,应该有 控制表示警报,退格,回车和换行的字符。 [...]
fgets
函数描述如下,在C99§7.19.7.2/ 2中:
fgets函数最多读取一个小于n指定的字符数 从流指向的流进入s指向的数组。 没有额外的 在新行字符(保留)之后或文件结束后读取字符。一个 在读入数组的最后一个字符后立即写入空字符。
因此,遇到字符串I am a boy\r\n
时,符合要求的实现应该读取\n
字符。实施应基于平台放弃\r
,这是不可能的理由。
答案 2 :(得分:1)
c 标准对中的文本流(以及其他内容)是这样说的:
<块引用>可能需要在输入和输出时添加、更改或删除字符以 符合在宿主中表示文本的不同约定 环境。因此,之间不需要一一对应 流中的字符和外部表示中的字符。数据读入 来自文本流的数据必然等于 仅在以下情况下才较早地写入该流: 数据仅包含打印 字符和控制字符水平制表符和换行符;没有换行 字符紧跟在空格字符之前;和最后一个字符 是换行符。
换句话说,如果一个文件在文本模式下打开,一个实现可以自由地添加、删除和修改控制字符,如果它想要/需要在往返磁盘时。这显然是 microsoft 实现对回车所做的,但 gnu 实现没有。