将字符串转换为整数的sscanf和atoi之间有什么区别?

将字符串转换为整数的sscanf和atoi之间有什么区别?,c,scanf,atoi,C,Scanf,Atoi,通用条款4.4.4 c89 最好将字符串转换为整数值 我尝试了两种不同的方法atoi和sscanf。两种方法都能如期工作 char digits[3] = "34"; int device_num = 0; if(sscanf(digits, "%d", &device_num) == EOF) { fprintf(stderr, "WARNING: Incorrect value for device\n"); return FALSE; } 或者使用atoi de

通用条款4.4.4 c89

最好将字符串转换为整数值

我尝试了两种不同的方法atoi和sscanf。两种方法都能如期工作

char digits[3] = "34";
int device_num = 0;

if(sscanf(digits, "%d", &device_num) == EOF) {
    fprintf(stderr, "WARNING: Incorrect value for device\n");
    return FALSE;
}
或者使用atoi

device_num = atoi(digits);
我认为sscanf会更好,因为您可以检查错误。但是,atoi不进行任何检查。

*scanf()
函数族返回转换的值的数量。因此,您应该检查以确保
sscanf()
在您的案例中返回1
EOF
返回“输入失败”,这意味着
ssacnf()
永远不会返回
EOF

对于
sscanf()
,函数必须解析格式字符串,然后解码整数
atoi()
没有这种开销。两者都存在值超出范围导致未定义行为的问题

您应该使用
strtol()
strtoul()
函数,它们提供了更好的错误检测和检查。它们还让您知道整个字符串是否已被使用


如果您想要一个
int
,您可以始终使用
strtol()
,然后检查返回值是否介于
int\u MIN
int\u MAX

之间。如果用户输入34abc并将其传递给atoi,则返回值将为34。 如果要验证输入的值,则必须对输入的字符串反复使用isdigit

您有3种选择:

  • atoi
  • 如果您在性能关键型代码中使用它,这可能是最快的,但它不会报告错误。如果字符串不是以整数开头,它将返回0。如果字符串在整数之后包含垃圾,它将转换初始部分并忽略其余部分。如果数字太大,无法放入
    int
    ,则行为未指定

  • sscanf
  • 一些错误报告,您可以灵活地选择存储的类型(char/short/int/long/long/long/size\t/ptrdiff\t/intmax\t的签名/未签名版本)

    返回值是成功的转换数,因此如果字符串不是以整数开头,则扫描
    “%d”
    将返回0。您可以使用
    “%d%n”
    存储在另一个变量中读取的整数之后的第一个字符的索引,从而检查整个字符串是否已转换,或者之后是否有垃圾。但是,与
    atoi
    一样,整数溢出的行为是未指定的

  • strtol
    和系列
  • 强大的错误报告功能,前提是您在拨打电话之前将
    errno
    设置为0。返回值在溢出时指定,并且将设置
    errno
    。您可以选择2到36之间的任何数字基数,或者指定0作为基数,以分别将前导
    0x
    0
    自动解释为十六进制和八进制。要转换为的类型选择是
    long/long-long/intmax\t
    的有符号/无符号版本

    如果需要较小的类型,您可以始终将结果存储在临时
    long
    unsigned long
    变量中,并自行检查溢出

    由于这些函数采用指向指针的指针参数,因此还可以免费获得指向转换后的整数后面的第一个字符的指针,因此可以判断整个字符串是否为整数,或者在需要时解析字符串中的后续数据


    就我个人而言,我推荐
    strtol
    系列用于大多数目的。如果你做的事情又快又脏,atoi可能会满足你的需要

    顺便说一句,有时我发现我需要解析那些不应该接受前导空格、符号等的数字。在这种情况下,很容易推出自己的for循环,例如

    for (x=0; (unsigned)*s-'0'<10; s++) 
        x=10*x+(*s-'0');
    

    致@R。。我认为在
    strtol
    调用中检查
    errno
    以检测错误是不够的

    long strtol (const char *String, char **EndPointer, int Base)
    

    您还需要检查端点是否有错误。

    。。皮克博伊的回答很简洁

    long strtol (const char *String, char **EndPointer, int Base)
    
    // examples
    strtol(s, NULL, 10);
    strtol(s, &s, 10);
    

    如果不担心无效字符串输入或范围问题,请使用最简单的:
    atoi()

    否则,具有最佳错误/范围检测的方法既不是
    atoi()
    ,也不是
    sscanf()
    。 all ready详细说明了使用
    atoi()
    进行错误检查的不足,以及使用
    sscanf()
    进行的一些错误检查

    strtol()
    是将字符串转换为
    int
    时最严格的函数。但这只是一个开始。下面是详细的例子来说明正确的用法,以及在后面给出这个答案的原因

    这类似于
    atoi()
    ,并且忽略了使用
    strtol()
    的错误检测功能

    要充分使用strtol(),需要考虑以下各种功能:

  • 未转换检测:示例:
    “xyz”
    ,或
    ”--0“
    ?在这些情况下,
    endptr
    将匹配
    nptr

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (nptr == endptr) return FAIL_NO_CONVERT;
    
  • 整个字符串应该转换还是只转换前导部分:
    “123xyz”
    可以吗

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '\0') return FAIL_EXTRA_JUNK;
    
  • 检测值是否太大,结果不能表示为
    长的
    ,如
    “999999999999999999999999999”

  • 检测该值是否超出
    int
    范围,但不在
    long
    范围内。如果
    int
    long
    具有相同的范围,则不需要进行此测试

    long L = strtol(nptr, &endptr, 10);
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
    

    注意:前面提到的所有函数都允许前导空格,这是一个可选的前导符号字符,并且受区域设置更改的影响。更严格的转换需要额外的代码



    注:非OP标题更改偏重强调。这个答案更适用于原始标题“将字符串转换为整数sscanf或atoi”

    作为
    strtol
    等的额外奖励,如果您将基数设置为
    0
    ,则会自动选择转换
    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '\0') return FAIL_EXTRA_JUNK;
    
    errno = 0;
    long L = strtol(nptr, &endptr, 10);
    if (errno == ERANGE) return FAIL_OVERFLOW;
    
    long L = strtol(nptr, &endptr, 10);
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
    
    #include <errno.h>
    #include <stdlib.h>
    
    int strtoi(const char *nptr, int *error_code) {
      char *endptr;
      errno = 0;
      long i = strtol(nptr, &endptr, 10);
    
      #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
      if (errno == ERANGE || i > INT_MAX || i < INT_MIN) {
        errno = ERANGE;
        i = i > 0 : INT_MAX : INT_MIN;
        *error_code = FAIL_INT_OVERFLOW;
      }
      #else
      if (errno == ERANGE) {
        *error_code = FAIL_OVERFLOW;
      }
      #endif
    
      else if (endptr == nptr) {
        *error_code = FAIL_NO_CONVERT;
      } else if (*endptr != '\0') {
        *error_code = FAIL_EXTRA_JUNK;
      } else if (errno) {
        *error_code = FAIL_IMPLEMENTATION_REASON;
      }
      return (int) i;
    }