Fgets在Linux和Windows中从同一文件写入不同的字符串

时间:2018-11-10 22:30:57

标签: c linux fgets

我刚遇到一个问题,我在Linux的valgrind和Windows cmd的其他测试之间进行切换。

我正在从这样的文件中读取某行:

fgets(buf, MAX_LINE_LEN, f_input);

当然,buf的大小为MAX_LINE_LEN + 1,但我离题了。

这是

的输出
printf("String length: %u; Contents: ", strlen(buf));
for (usint i = 0; i < strlen(buf); i++)
  printf("%x ", buf[i]);
puts(";");

在Windows中:

String length: 14; Contents: 41 6e 64 72 65 6a 20 50 6c 61 76 6b 61 a ;
String length: 22; Contents: 41 6e 6e 61 20 4d 61 72 69 61 20 43 69 63 6d 61 6e 63 6f 76 61 a ;
String length: 25; Contents: 4d 61 72 69 61 20 52 61 7a 75 73 6f 76 61 20 4d 61 72 74 61 6b 6f 76 61 a ;
String length: 24; Contents: 4d 69 6c 61 6e 20 52 61 73 74 69 73 6c 61 76 20 50 6f 6b 6f 6a 6e 79 a ;
String length: 21; Contents: 4d 69 6c 65 6e 61 20 53 65 64 6d 69 6b 72 61 73 6b 6f 76 61 a ;
String length: 15; Contents: 56 69 6e 63 65 6e 74 20 53 69 6b 75 6c 61 a ;
String length: 17; Contents: 56 69 6e 63 65 6e 74 20 76 61 6e 20 47 6f 67 68 a ;

和在Linux中:

String length: 15; Contents: 41 6e 64 72 65 6a 20 50 6c 61 76 6b 61 d a ;
String length: 23; Contents: 41 6e 6e 61 20 4d 61 72 69 61 20 43 69 63 6d 61 6e 63 6f 76 61 d a ;
String length: 26; Contents: 4d 61 72 69 61 20 52 61 7a 75 73 6f 76 61 20 4d 61 72 74 61 6b 6f 76 61 d a ;
String length: 25; Contents: 4d 69 6c 61 6e 20 52 61 73 74 69 73 6c 61 76 20 50 6f 6b 6f 6a 6e 79 d a ;
String length: 22; Contents: 4d 69 6c 65 6e 61 20 53 65 64 6d 69 6b 72 61 73 6b 6f 76 61 d a ;
String length: 16; Contents: 56 69 6e 63 65 6e 74 20 53 69 6b 75 6c 61 d a ;
String length: 18; Contents: 56 69 6e 63 65 6e 74 20 76 61 6e 20 47 6f 67 68 d a ;

在Linux中可以看到,NL之前还有另一个字符,即回车符。如果有人可以解释这一点,并且省去了为Linux和Windows代码添加ifdef语句的麻烦,我将不胜感激。我了解,Linux在每一行之后都附加了一个回车符,但这真的是预期的行为,当它随后被fgets读取时会出现这种情况吗?

3 个答案:

答案 0 :(得分:2)

MS和Linux对文本文件行结尾的期望不同:"\r\n""\n"

为应对,建议在fgets()之后使用strcspn()终止可能的行序结束,无论是"\n""\r\n"还是缺失。

fgets(buf, MAX_LINE_LEN, f_input);
buf[strcspn(buf, "\n\r")] = '\0';

Windows上的某些编译器将使用"\n"作为行尾序列,而其他编译器将使用"\r\n"。因此,我将这种变化归因于编译器及其制造商,而不是OS。另外,一些旧的MAC文本文件以'\r'结尾,并且在Linux上会污染fgets()

进一步:如果读取一个完整的缓冲区,则读取"\r\n"作为文本文件并期望"\n"作为行尾序列的文件时出现问题"......\r",并且在下一个"\n"上,行剩余为fgets()。当缓冲区不足以输入 line 时,就需要进行额外的处理。

一个变体的文本文件经常被复制到其他平台上,因此这很少发生。

由于进行编辑,某些文本文件将混合使用行尾序列。

pedantic代码会将文件读取为二进制文件,并且进程变体行结尾本身没有fgets()。祝你好运。

答案 1 :(得分:2)

  

在Linux中可以看到,NL之前还有另一个字符,即回车符。

这是因为您的文件使用CR + LF换行符,即每个换行符实际上是两个字符:"\r\n"

如果在Windows中打开不带"b"标志的文件,则其C库会将您写入的每个\n转换为\r\n,并将您读取的每个\r\n转换为{{ 1}}。

在Windows中使用\n fopen()标志查看实际的文件内容。

使用"b"阅读一行时,可以使用fgets(buf, sizeof buf, handle)删除换行符。

答案 2 :(得分:1)

在C中,您以 text binary 模式打开文件流。在二进制模式下,不进行任何转换,输入和输出是文件中的字节。在文本模式下,C“换行符”字符被转换为相关平台上的常用字符。一个类UNIX系统,这是一个0A字节,而在DOS类系统中,这是一个0D字节,后跟一个0A字节。在此处列出的其他操作系统上还有其他情况:

https://en.wikipedia.org/wiki/Newline

这样一来,您就不必在每个程序中都应对每种不同的文本格式,只要在默认情况下(文本模式),C程序就会将所有这些格式转换为\n字符。输入/输出层为您完成必要的翻译。

当您使用fopen()在C中打开文件流以进行读取或写入时,您将提供一个“文件模式”参数-您可能在此处将其用作"r"来读取文件,或"w"写一个。如果要完成换行翻译,则可以指定以 binary 模式打开流,其中"rb"用于读取,"wb"用于写入。