C语言fscanf使用[^ ]格式符时会自动处理\r回车符吗?
问题描述
我正在使用如下文本文件测试fscanf:
1 George Washington 2 John Adams ... 9 William Henry Harrison ... 46 Joseph R. Biden Jr.
使用如下struct:
struct president_of_united_state { unsigned char presidency; char president[23]; // 9. William Henry Harrison (22) };
我想要理解搭配[^\n]使用时fscanf的行为:
void set_presidents_of_united_states(struct president_of_united_state *const p) { assert(p != NULL); static const char FILENAME_PRESIDENTS_OF_UNITED_STATES[] = "presidents_of_united_states.txt"; FILE *stream = fopen(FILENAME_PRESIDENTS_OF_UNITED_STATES, "r"); if (stream == NULL) { fprintf(stderr, "failed to open %s\n", FILENAME_PRESIDENTS_OF_UNITED_STATES); return; } for (int i = 0; i < NUMBER_OF_PRESIDENTS_OF_UNITED_STATES; i++) { int const n = fscanf(stream, "%hhu %22[^\n]", &p[i].presidency, p[i].president); assert(n == 2); } if (fclose(stream) == EOF) { fprintf(stderr, "failed to close the stream"); } }
当行分隔符仅为0a(\n)时:
3120 4765 6f72 6765 2057 6173 6869 6e67 1 George Washing 746f 6e0a 3220 4a6f 686e 2041 6461 6d73 ton.2 John Adams
程序运行符合预期,读取到的字符数组内容如下:
[7] = {char} 87 'W' [8] = {char} 97 'a' [9] = {char} 115 's' [10] = {char} 104 'h' [11] = {char} 105 'i' [12] = {char} 110 'n' [13] = {char} 103 'g' [14] = {char} 116 't' [15] = {char} 111 'o' [16] = {char} 110 'n' [17] = {char} 0 '\000'
[0] = {char} 74 'J' [1] = {char} 111 'o' [2] = {char} 104 'h' [3] = {char} 110 'n' [4] = {char} 32 ' ' [5] = {char} 65 'A' [6] = {char} 100 'd' [7] = {char} 97 'a' [8] = {char} 109 'm' [9] = {char} 115 's' [10] = {char} 0 '\000'
当行分隔符为0d(\r)加0a(\n)时:
3120 4765 6f72 6765 2057 6173 6869 6e67 1 George Washing 746f 6e0d 0a32 204a 6f68 6e20 4164 616d ton..2 John Adam
程序也正常运行,读取到的字符数组内容如下:
[7] = {char} 87 'W' [8] = {char} 97 'a' [9] = {char} 115 's' [10] = {char} 104 'h' [11] = {char} 105 'i' [12] = {char} 110 'n' [13] = {char} 103 'g' [14] = {char} 116 't' [15] = {char} 111 'o' [16] = {char} 110 'n' [17] = {char} 0 '\000'
[0] = {char} 74 'J' [1] = {char} 111 'o' [2] = {char} 104 'h' [3] = {char} 110 'n' [4] = {char} 32 ' ' [5] = {char} 65 'A' [6] = {char} 100 'd' [7] = {char} 97 'a' [8] = {char} 109 'm' [9] = {char} 115 's' [10] = {char} 0 '\000'
我的问题是:fscanf是否自动忽略了\r字符?
解答
不是fscanf自动忽略了\r,这个效果来自C标准库文本模式打开文件的默认行为。
你调用fopen时使用了"r"模式,也就是文本读模式。在Windows平台下,文本模式会自动将磁盘上存储的\r\n换行序列转换为内存中的单个\n,所以读取操作完成后你在程序中完全看不到\r字符;反之如果是文本写模式,库也会自动将程序输出的\n转换为磁盘上的\r\n存储。
如果想要原样读取文件的所有字节,需要使用二进制模式打开文件,也就是把fopen的第二个参数改为"rb",这时你就会读取到\r字符,现有格式化字符串%hhu %22[^\n]会把\r也读进president数组的末尾,和现在的运行结果就会有差异。
另外补充:你当前代码的格式化字符串读完[^\n]后会停在\n的位置,下一次循环%hhu前面的空格会自动跳过所有空白字符(包括\n),所以可以正常读取下一行的序号,这个逻辑是符合预期的。
内容的提问来源于stack exchange,提问作者Jin Kwon
相关产品推荐
相关产品推荐

