繁体   English   中英

以下C代码中scanset的不同行为

[英]Different behaviour of scanset in following C code

对于scanset中的0-9范围,如果输入字符串12345thisisnice输出是12345,这是正确的,但是对于输入thisisnice12345输出是a @,这是错误的,我认为,为什么这是错误的?

/* A simple scanset example */
#include <stdio.h> 

int main(void) 
{ 
    char str[128]; 

    printf("Enter a string: "); 
    scanf("%[9-0]s", str); 

    printf("You entered: %s\n", str); 

    return 0; 
} 

我希望thisisnice12345的o / p为12345,但实际的o / p是a @

这是您程序的改进版本:

#include <stdio.h> 

int main(void) 
{ 
    char str[128];

    printf("Enter a string: "); 
    if(scanf("%[0-9]", str) == 1)
         printf("You entered: \"%s\"\n", str);
    else printf("You entered nothing.\n");

    return 0; 
} 

正确的scanset是%[0-9] ,顺序为数字,后跟s (在这种情况下,尾随s是无害的,但是在其他情况下会引起问题。)更重要的是,如果scanf返回0,则表明它不匹配,因此在str没有存储任何内容。

您的代码具有未定义的行为。 您没有初始化str ,并且第二个输入scanf没有读取任何字段(“ thisisnice12345”不是以数字开头 ),因此不写入str 如果随后是printf -ing str ,则您正在读取未初始化的变量,该变量是未定义的行为。

您需要检查scanf的返回值。 仅在scanf返回1时才写入str

scanf函数系列无法执行复杂的正则表达式 但是它有一些功能。 如果要阅读用户输入的第一个数字,可以使用:

  • %[0-9]仅读取一个数字(警告其0-9而不是9-0
  • %[^0-9]读取除数字以外的所有数字
  • %*[^0-9]读取除数字以外的所有内容,并忽略它。

所以你可以这样写:

/* A simple scanset example */
#include <stdio.h> 

int main(void) 
{ 
    char str[128]; 
    int ret;

    printf("Enter a string: "); 

    /* Try to read a number at input start */
    ret = scanf("%[0-9]", str);     

    if (1 != ret){
        /* if reading failed, try to re-read the input, 
           ignoring all that is before the number */
        ret = scanf("%*[^0-9]%[0-9]", str); 
    }

    if (1 != ret)
        printf("no match");
    else
        printf("You entered: %s\n", str); 

    return 0; 
} 

如注释中所指出的,如果读取大小受到限制,则此代码会更好:

  • %8[0-9]scanf限制为只能读取8个数字字符。


#include <stdio.h> 

int main(void) 
{ 
    char str[8+1]; 
    int ret;

    printf("Enter a string: "); 

    /* Try to read a number at input start */
    ret = scanf("%8[0-9]", str);     

    if (1 != ret){
        /* if reading failed, try to re-read the input, 
           ignoring all that is before the number */
        ret = scanf("%*[^0-9]%8[0-9]", str); 
    }

    if (1 != ret)
        printf("no match");
    else
        printf("You entered: %s\n", str); 

    return 0; 
} 

但是在这段代码中,您将不得不处理一个新的事实:如果用户输入的数字太大,该如何处理? strlen可以提供帮助,或strto[u]l ...)

Enter a string: 1234567891011

You entered: 12345678

首先:字符s不属于scanset指定符。 %[...] ,不是%[...]s


现在C标准说

  • 如果-字符位于扫描列表中,并且不是第一个字符,也不是第二个字符(第一个字符是^ ,也不是最后一个字符,则该行为是实现定义的。

所以行为

scanf("%[9-0]", str); 

是实现定义的。 允许实现的行为类似于%[-09]或以任何其他实现定义的方式运行。

现在,例如, Glibc记录了连字符表示字符范围,但是POSIX将其留给实现。 即使这样, %[9-0]是否具有该范围内的所有字符的问题还是值得怀疑的。 总而言之,最好不要编写任何这样的代码。 如果不指定缓冲区长度, scanf很危险。 由于您被一个兼容性错误所困扰,因此最好不要再猜测其他平台上的行为,而只需编写

int rv = scanf("%127[0123456789]", str);
if (rv == 1) {
    ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM