我刚遇到一个问题,我在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
读取时会出现这种情况吗?
答案 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"
用于写入。