- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我用 C99 编写了一个完整的应用程序,并在两个基于 GNU/Linux 的系统上对其进行了全面测试。当尝试在 Windows 上使用 Visual Studio 编译它导致应用程序行为异常时,我感到很惊讶。起初我无法断言哪里出了问题,但我尝试使用 VC 调试器,然后我发现了关于 stdio.h
中声明的 fscanf()
函数的差异.
下面的代码足以说明问题:
#include <stdio.h>
int main() {
unsigned num1, num2, num3;
FILE *file = fopen("file.bin", "rb");
fscanf(file, "%u", &num1);
fgetc(file); // consume and discard \0
fscanf(file, "%u", &num2);
fgetc(file); // ditto
fscanf(file, "%u", &num3);
fgetc(file); // ditto
fclose(file);
printf("%d, %d, %d\n", num1, num2, num3);
return 0;
}
假设 file.bin 恰好包含 512\0256\0128\0
:
$ hexdump -C file.bin
00000000 35 31 32 00 32 35 36 00 31 32 38 00 |512.256.128.|
现在,当在 Ubuntu 机器上使用 GCC 4.8.4 编译时,生成的程序会按预期读取数字并将 512, 256, 128
打印到标准输出。
在 Windows 上使用 MinGW 4.8.1 编译它会得到相同的预期结果。
但是,当我使用 Visual Studio Community 2015 编译代码时,似乎存在重大差异;即,输出是:
512, 56, 28
如您所见,fscanf()
已使用尾随空字符,因此 fgetc()
捕获并丢弃对数据完整性至关重要的字符。
注释掉 fgetc()
行可以使代码在 VC 中运行,但在 GCC(可能还有其他编译器)中会破坏它。
这是怎么回事,我如何将其转换为可移植的 C 代码?我遇到了未定义的行为吗?请注意,我假设使用 C99 标准。
最佳答案
TL;DR:您遇到了 MSVC 不一致问题,这是一个长期存在的问题,MS 从未对解决它表现出太大兴趣。如果除了符合 C 实现之外还必须支持 MSVC,那么一种方法是在通过 MSVC 编译程序时使用条件编译指令来抑制 fgetc()
调用。
我倾向于同意通过格式化 I/O 函数读取二进制数据是一个有问题的计划的评论。然而,更值得怀疑的是,
compil[ing] it using Visual Studio on Windows
和
assuming the C99 standard.
据我所知,没有 版本的 MSVC 符合 C99。最新版本可能在符合 C2011 方面做得更好,部分原因是 C2011 使一些在 C99 中是强制性的功能成为可选功能。
但是,无论您使用的是哪个版本的 MSVC,我都认为它不符合这方面的标准(C99 和 C2011)。这是来自C99, section 7.19.6.2的相关文本
A conversion specification is executed in the following steps:
[...]
An input item is read from the stream [...]. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence. The first character, if any, after the input item remains unread.
标准非常明确,第一个与输入序列不匹配的字符保持未读状态,因此可以认为 MSVC 符合标准的唯一方法是 \0
字符是否可以被解释为一部分匹配输入序列的(和终止),或者如果 fgetc()
被允许跳过 \0
字符。我认为后者没有理由,特别是考虑到流是以二进制模式打开的,所以让我们考虑前者。
对于 u
转换说明符,匹配的输入序列是 defined作为一个
Matches an optionally signed decimal integer, whose format is the same as expected for the subject sequence of the strtoul function with the value 10 for the base argument.
定义了“strtoul 函数的主题序列”in that function's specifications :
First, they decompose the input string into three parts: an initial, possibly empty, sequence of white-space characters (as specified by the isspace function), a subject sequence resembling an integer represented in some radix determined by the value of base, and a final string of one or more unrecognized characters, including the terminating null character of the input string.
请特别注意,终止空字符明确归因于无法识别的字符的最终字符串。它不是主题字符串的一部分,因此当 fscanf()
根据 u
说明符转换输入时,它不应与 fscanf()
匹配。
关于c - fscanf() 跨不同编译器的不一致行为(消耗尾随空字符),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42420784/
我正在用 C 编写一个函数,它应该从文件中输入有关学生及其成绩的信息。但是,有时在运行程序时,即使输出符合预期,测试也会在 fscanf 处显示分析器错误。 /*structure for grade
如何在 fscanf() 中跳过一个值并转到下一个值?例如,我的输入文件中有以下数据: 11112222 3.95 4 22.5 我应该怎么做才能扫描第二个值? (我想跳过11112222扫描3.95
我正在使用这个 for 循环从纬度和经度坐标文件中引入数据,然后将坐标转换为十进制度数,将度数转换为弧度,然后计算分离角度,并返回两个城市之间的距离。我需要比较文件的前两行,计算距离,打印结果,然后再
我在 while(fscanf != EOF) 循环中执行了一些代码。但是,即使 fscanf 已完成执行,我也需要继续运行该代码,直到满足某些条件。我的意思是我想我可以将代码复制/粘贴到 while
我对 C 语言的文件处理非常陌生,我想问几个问题! 我经常使用 fscanf/fget 将文件转换为不同的格式。不过我不太清楚 fscanf 和 fgets 之间的区别。 特别是,我不确定 fscan
我是 C 的新手,这里有这个简单的代码: int main(int argc, char **argv){ FILE *input = stdin; char string[20];
我已经有了一些使用 fscanf() 读取文本文件的代码,现在我需要修改它,以便以前无空格的字段需要允许空格。文本文件基本上是这样的形式: title: DATA title: DATA etc...
我目前正在开发一个简单的 C 应用程序。它采用单个文件作为命令行参数,其格式如下: 1,2,3 4,5,6 7,8,9 etc. 但是,无论出于何种原因,fscanf 都不会扫描数字!这是一个例子:
我正在尝试读取逗号分隔值格式的文件,程序读取了前两种数据类型,但无法读取最后一种。请帮我一些忙 这是我正在尝试读取的文件中的内容: Jane,50,400.60 代码如下: FILE* fpt
我试图让我的程序从文件中读取输入,但不跳过 fscanf 通常执行的空行。扫描文件的文件和循环是: inFile = fopen("text.txt", "r"); for(i = 0; fsca
我正在尝试从 C 文件中读取数据,该文件将始终以以下类型的行进行格式化: 16 Oct 2013 00:01:00.000,0.000,0.000000 这是一个字符串、一个逗号、一个浮点数、一个逗号
如何格式化 fscanf 来格式化输入{'name surname', 'username', 指向不包含撇号的字符串 fscanf(fp,"{%s %s %d}",name,username,use
我正在尝试使用 fscanf 将文件中的行读取到指向字符数组的指针中。我在打印时遇到段错误。我究竟做错了什么?我应该使用 fscanf 以外的函数吗? #include #include #inc
我有一个制表符分隔文件,我正在尝试将其转换为制表符分隔文件。我正在使用 C。我在尝试读取文件的第二行时遇到了困难。现在我只有数万行重复第一行。 #include #include #define
我正在尝试使用 c 读取一个文件,其中每个变量都由竖线字符分隔。我尝试了以下 fscanf(fp, "%s[|], %s[|], \n", str1, str2); 括号之间的字符是垂直线:竖线字符。
我真的卡在某事上了。 我有一个文本文件,其中有 1 个单词后跟 ~100 个 float 。 float 由空格、制表符或换行符分隔。这种格式在整个文本文件中重复多次。 例如,这是文本文件的样子: o
我想使用 fscanf (使用 gcc 的 C 代码)从文件中解析 ip。所以,我想做: char myip[INET_ADDRSTRLEN]; fscanf(file, "%16s", myip);
while 循环不起作用,我不明白为什么。它没有给我任何错误,但它没有读取任何输入内容。文件内容为: 4 $11$ pelle $2$ pollo $333$ palla $41$ alla 我的代码
我有一些代码,我试图从文件中读取一个集团实例。实例文件中的第一行和第二行分别表示顶点和边的数量。但是我的代码似乎没有正确阅读。这是我的代码中与问题相关的部分: int num_edges=0; int
我正在尝试使用 fscanf 解析一个文本 (CSS) 文件并提取所有匹配此模式的语句: @import "some/file/somewhere.css"; 为此,我设置了以下循环: FILE *f
我是一名优秀的程序员,十分优秀!