注意:我完全修改了该问题,以更正确地反射(reflect)我为赏金设置的内容。请原谅可能与该答案产生的所有不一致之处。我不想创建一个新问题,因为以前对此的回答可能会有所帮助。
我正在努力实现C标准库,并对标准的一个特定方面感到困惑。
该标准根据scanf
,strtol
和strtoul
的定义,定义了strtod
函数家族接受的数字格式(%d,%i,%u,%o,%x)。
该标准还规定fscanf()
仅将最多一个字符放回输入流,因此strtol
不接受strtoul
,strtod
和fscanf
接受的某些序列(ISO/IEC 9899:1999,脚注251)。
我试图找到一些可以体现出这种差异的值(value)观。事实证明,十六进制前缀“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
*/
最佳答案
在comp.std.c上与PL22.11(ANSI“C”)的副字符Fred J.Tydeman的通信对此有所了解:
fscanf
这使“0x”成为匹配输入序列前缀的最长序列。 (即使是%i
转换,因为十六进制“0x”的序列比十进制“0”的序列还要长。)
这使fscanf
读取“z”,并将其放回不匹配的位置(遵守脚注251的一个字符的推回限制)。
这会使“0x”不匹配,即fscanf
应该不分配任何值,返回零(如果%x
或%i
是第一个转化说明符),而将“z”保留为输入流中的第一个未读字符。
strtol
strtol
(和strtoul
)的定义在一个关键点上有所不同:
这意味着strtol
应该寻找最长的有效序列,在这种情况下为“0”。它应该将endptr
指向“x”,并返回零。
关于c - 解析数字时scanf()和strtol()/strtod()之间的区别,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1425730/