解析数字时scanf()和strtol()/ strtod()之间的区别

Dev*_*lar 12 c standards standards-compliance

注意:我完全重写了这个问题,以更恰当地反映出我为此设置的赏金.请原谅与已经给出的答案有任何不一致之处.我不想创建一个新问题,因为之前的答案可能会有所帮助.


我正在努力实现一个C标准库,并对标准的一个特定角落感到困惑.

该标准定义了由接受数字格式scanf函数族(%d,%I,%U,%O,%X)在用于定义而言strtol,strtoul,和strtod.

该标准还规定,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
*/
Run Code Online (Sandbox Code Playgroud)

Dev*_*lar 6

与comp.std.c的PL22.11(ANSI"C")副主席Fred J. Tydeman的沟通对此有所了解:

fscanf

输入项被定义为输入字符的最长序列,它是匹配输入序列的前缀,或者是匹配输入序列的前缀.(7.19.6.2 P9)

这使得"0x"成为匹配输入序列的前缀的最长序列.(即使进行%i转换,十六进制"0x"的序列也比十进制"0"长.)

输入项目之后的第一个字符(如果有)仍未读取.(7.19.6.2 P9)

这使得fscanf读取"z",并将其作为不匹配(遵守脚注251的单字符后推限制)).

如果输入项不是匹配序列,则指令的执行失败:此条件是匹配失败.(7.19.6.2 P10)

这使得"0x"无法匹配,即fscanf不应分配任何值,返回零(如果%x或是%i第一个转换说明符),并将"z"保留为输入流中的第一个未读字符.

strtol

strtol(和strtoul)的定义在一个关键点上有所不同:

主题序列被定义为输入字符串的最长初始子序列,从第一个非空白字符开始,即预期形式.(7.20.1.4 P4,强调我的)

这意味着strtol应该寻找最长的有效序列,在本例中为"0".它应该指向endptr"x",并返回零作为结果.