68

gcc 4.4.4 c89

将字符串转换为整数值更好。

我尝试了 2 种不同的方法 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 不做任何检查。

4

6 回答 6

117

您有 3 个选择:

  1. atoi

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

  1. sscanf

一些错误报告,并且您可以灵活地存储什么类型(签名/未签名版本char/short/int/long/long long/size_t/ptrdiff_t/intmax_t)。

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

  1. strtol和家人

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

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

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


就个人而言,我会推荐这个strtol家庭用于大多数目的。如果您正在做一些快速而肮脏的事情,atoi 可能会满足您的需求。

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

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

或者您可以使用(为了稳健性):

if (isdigit(*s))
    x=strtol(s, &s, 10);
else /* error */ 
于 2010-08-06T06:45:35.853 回答
10

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

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

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

如果你想要一个int,你总是可以使用strtol(),然后检查返回的值,看看它是否介于INT_MINand之间INT_MAX

于 2010-08-06T02:37:33.410 回答
4

致@R ..我认为在通话中检查errno错误检测是不够的。strtol

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

您还需要检查EndPointer错误。

于 2012-03-15T06:27:39.930 回答
2

为简洁起见,结合 R.. 和 PickBoy 答案

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

// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);
于 2012-04-10T06:57:14.463 回答
2

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

否则,具有最佳错误/范围检测的方法既不是atoi(),也不是sscanf()This good answer all ready详细说明了缺少错误检查atoi()一些错误检查sscanf()

strtol()是将字符串转换为int. 然而,这只是一个开始。下面是详细的示例,以显示正确的用法以及接受后的答案的原因。

// Over-simplified use
int strtoi(const char *nptr) {
  int i = (int) strtol(nptr, (char **)NULL, 10);
  return i; 
}

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

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

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

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

    char *endptr;
    int i = (int)strtol(nptr, &endptr, 10);
    if (*endptr != '\0') return FAIL_EXTRA_JUNK;
    
  3. 检测 value 是否太大,结果不能表示为longlike "999999999999999999999999999999"

    errno = 0;
    long L = strtol(nptr, &endptr, 10);
    if (errno == ERANGE) return FAIL_OVERFLOW;
    
  4. 检测该值是否超出范围 than int,但不是long。如果intlong具有相同的范围,则不需要此测试。

    long L = strtol(nptr, &endptr, 10);
    if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
    
  5. 一些实现超出了 C 标准并errno出于其他原因进行设置,例如errno 到 EINVAL,以防未执行转换EINVAL Base 参数的值无效。. 测试这些errno值的最佳时间取决于实现。

把这一切放在一起:(根据您的需要调整)

#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;
}

注意:所有提到的函数都允许前导空格、可选的前导符号字符,并且受语言环境更改的影响。更严格的转换需要额外的代码。


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

于 2015-12-14T04:49:12.673 回答
0

如果用户输入 34abc 并将它们传递给 atoi 它将返回 34。如果要验证输入的值,则必须在输入的字符串上迭代地使用 isdigit

于 2010-08-06T02:43:25.753 回答