scanf()和strtol()/strtod()在解析数字方面的差异
注意:我完全修改了这个问题,以更恰当地反映我设置奖金的目的。请原谅与已经给出的答案之间可能产生的任何不一致。我不想提出一个新问题,因为以前对这个问题的回答可能会有所帮助scanf()和strtol()/strtod()在解析数字方面的差异,c,standards,standards-compliance,C,Standards,Standards Compliance,注意:我完全修改了这个问题,以更恰当地反映我设置奖金的目的。请原谅与已经给出的答案之间可能产生的任何不一致。我不想提出一个新问题,因为以前对这个问题的回答可能会有所帮助 我正在致力于实现一个C标准库,对该标准的一个具体方面感到困惑 本标准根据strtol、strtoul和strtod的定义定义了scanf功能系列(%d,%i,%u,%o,%x)所接受的数字格式 该标准还规定,fscanf()最多只能将一个字符放回输入流,因此strtol、strtoul和strtod接受的某些序列是fscanf
我正在致力于实现一个C标准库,对该标准的一个具体方面感到困惑 本标准根据
strtol
、strtoul
和strtod
的定义定义了scanf
功能系列(%d,%i,%u,%o,%x)所接受的数字格式
该标准还规定,fscanf()
最多只能将一个字符放回输入流,因此strtol
、strtoul
和strtod
接受的某些序列是fscanf
无法接受的(ISO/IEC 9899:1999,脚注251)
我试图找到一些能表现出这种差异的价值观。结果表明,十六进制前缀“0x”后跟一个非十六进制数字的字符,这是两个函数族不同的一种情况
有趣的是,显然没有两个可用的C库在输出上达成一致。(参见本问题末尾的测试程序和示例输出。)
我想听到的是在解析“0xz”时什么是标准兼容行为?。理想情况下,引用标准中的相关部分来说明问题
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{
int i, count, rc;
unsigned u;
char * endptr = NULL;
char culprit[] = "0xz";
/* File I/O to assert fscanf == sscanf */
FILE * fh = fopen( "testfile", "w+" );
fprintf( fh, "%s", culprit );
rewind( fh );
/* fscanf base 16 */
u = -1; count = -1;
rc = fscanf( fh, "%x%n", &u, &count );
printf( "fscanf: Returned %d, result %2d, consumed %d\n", rc, u, count );
rewind( fh );
/* strtoul base 16 */
u = strtoul( culprit, &endptr, 16 );
printf( "strtoul: result %2d, consumed %d\n", u, endptr - culprit );
puts( "" );
/* fscanf base 0 */
i = -1; count = -1;
rc = fscanf( fh, "%i%n", &i, &count );
printf( "fscanf: Returned %d, result %2d, consumed %d\n", rc, i, count );
rewind( fh );
/* strtol base 0 */
i = strtol( culprit, &endptr, 0 );
printf( "strtoul: result %2d, consumed %d\n", i, endptr - culprit );
fclose( fh );
return 0;
}
/* newlib 1.14
fscanf: Returned 1, result 0, consumed 1
strtoul: result 0, consumed 0
fscanf: Returned 1, result 0, consumed 1
strtoul: result 0, consumed 0
*/
/* glibc-2.8
fscanf: Returned 1, result 0, consumed 2
strtoul: result 0, consumed 1
fscanf: Returned 1, result 0, consumed 2
strtoul: result 0, consumed 1
*/
/* Microsoft MSVC
fscanf: Returned 0, result -1, consumed -1
strtoul: result 0, consumed 0
fscanf: Returned 0, result 0, consumed -1
strtoul: result 0, consumed 0
*/
/* IBM AIX
fscanf: Returned 0, result -1, consumed -1
strtoul: result 0, consumed 1
fscanf: Returned 0, result 0, consumed -1
strtoul: result 0, consumed 1
*/
#包括
#包括
#包括
int main()
{
int i,计数,rc;
未签名的u;
char*endptr=NULL;
罪魁祸首[]=“0xz”;
/*断言fscanf==sscanf的文件I/O*/
文件*fh=fopen(“测试文件”,“w+”);
fprintf(fh,“%s”,罪犯);
倒带(fh);
/*fscanf基地16*/
u=-1;计数=-1;
rc=fscanf(fh、%x%n、&u和计数);
printf(“fscanf:返回%d,结果%2d,消耗%d\n”,rc,u,count);
倒带(fh);
/*斯特图尔基地16*/
u=strtoul(罪魁祸首和endptr,16);
printf(“strtoul:结果%2d,消耗%d\n”,u,endptr-罪魁祸首);
认沽权(“”);
/*fscanf基0*/
i=-1;计数=-1;
rc=fscanf(fh、%i%n、&i和计数);
printf(“fscanf:Returned%d,result%2d,consumered%d\n”,rc,i,count);
倒带(fh);
/*strtol基0*/
i=strtol(罪魁祸首和endptr,0);
printf(“strtoul:结果%2d,消耗%d\n”,i,endptr-罪魁祸首);
fclose(fh);
返回0;
}
/*新图书馆1.14
fscanf:返回1,结果0,消耗1
strtoul:结果0,消耗0
fscanf:返回1,结果0,消耗1
strtoul:结果0,消耗0
*/
/*glibc-2.8
fscanf:返回1,结果0,消耗2
strtoul:结果0,消耗1
fscanf:返回1,结果0,消耗2
strtoul:结果0,消耗1
*/
/*微软MSVC
fscanf:返回0,结果-1,已使用-1
strtoul:结果0,消耗0
fscanf:返回0,结果0,消耗-1
strtoul:结果0,消耗0
*/
/*IBM AIX
fscanf:返回0,结果-1,已使用-1
strtoul:结果0,消耗1
fscanf:返回0,结果0,消耗-1
strtoul:结果0,消耗1
*/
我不确定我是否理解这个问题,但有一点是scanf()应该处理EOF。scanf()和strtol()是不同种类的野兽。也许你应该比较strtol()和sscanf()?我认为解析不允许产生不同的结果。Plaugher参考文献只是指出,strtol()
实现可能是一个不同的、更高效的版本,因为它可以完全访问整个字符串。重写问题后,答案就过时了。不过注释中有一些有趣的链接
如果有疑问,写一个测试谚语 在测试了我能想到的所有转换说明符和输入变量组合之后,我可以说这两个函数族没有给出相同的结果是正确的。(至少在glibc中,这是我可以测试的。) 当三种情况同时出现时,就会出现差异:
“%i”
或“%x”
(允许十六进制输入)“0x”
十六进制前缀#include <stdio.h>
#include <stdlib.h>
int main()
{
char * string = "0xz";
unsigned u;
int count;
char c;
char * endptr;
sscanf( string, "%x%n%c", &i, &count, &c );
printf( "Value: %d - Consumed: %d - Next char: %c - (sscanf())\n", u, count, c );
i = strtoul( string, &endptr, 16 );
printf( "Value: %d - Consumed: %td - Next char: %c - (strtoul())\n", u, ( endptr - string ), *endptr );
return 0;
}
这使我困惑。显然,sscanf()
不能在'x'
处退出,否则它将无法解析任何前缀为十六进制的“0x”
。因此,它读取了'z'
,发现它不匹配。但它决定只使用前导的“0”
作为值。这意味着将'z'
和'x'
推回。(是的,我知道sscanf()
所以。。。一个字符ungetc()
并不是真正的原因,这里…?:-/
是,结果不同。尽管如此,我仍然无法正确解释-(根据C99规范,scanf()
函数族解析整数的方式与strto*()
函数族解析整数的方式相同。例如,对于转换说明符x
,其内容如下:
匹配可选签名的
十六进制整数,其格式为
与预期的主题相同
strtoul
功能的顺序
base
参数的值16
因此,如果sscanf()
和strtoul()
给出不同的结果,那么libc实现就不符合要求<
Value: 0 - Consumed: 1 - Next char: x - (sscanf())
Value: 0 - Consumed: 0 - Next char: 0 - (strtoul())
scanf("%d", &x);
ungetc('9', stdin);
scanf("%d", &y);
printf("%d, %d\n", x, y);