308

我试图找出在 C 中是否有另一种将字符串转换为整数的方法。

我经常在我的代码中设计以下内容。

char s[] = "45";

int num = atoi(s);

那么,有没有更好的方法或其他方法?

4

14 回答 14

222

strtol哪个更好的IMO。我也很喜欢strtonum,所以如果你有它就使用它(但请记住它不是便携式的):

long long
     strtonum(const char *nptr, long long minval, long long maxval,
     const char **errstr);

您可能还对C99strtoumaxstrtoimax的标准函数感兴趣。例如你可以说:

uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
    /* Could not convert. */

无论如何,远离atoi

调用 atoi(str) 应等效于:

(int) strtol(str, (char **)NULL, 10)

除了错误的处理可能不同。如果无法表示该值,则行为是 undefined

于 2011-08-11T06:32:04.800 回答
36

强大的基于 C89strtol的解决方案

和:

  • 没有未定义的行为(与atoi家人一样)
  • 比整数更严格的定义strtol(例如,没有前导空格或尾随垃圾字符)
  • 错误案例的分类(例如向用户提供有用的错误消息)
  • 一个“测试套件”
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

typedef enum {
    STR2INT_SUCCESS,
    STR2INT_OVERFLOW,
    STR2INT_UNDERFLOW,
    STR2INT_INCONVERTIBLE
} str2int_errno;

/* Convert string s to int out.
 *
 * @param[out] out The converted int. Cannot be NULL.
 *
 * @param[in] s Input string to be converted.
 *
 *     The format is the same as strtol,
 *     except that the following are inconvertible:
 *
 *     - empty string
 *     - leading whitespace
 *     - any trailing characters that are not part of the number
 *
 *     Cannot be NULL.
 *
 * @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
 *
 * @return Indicates if the operation succeeded, or why it failed.
 */
str2int_errno str2int(int *out, char *s, int base) {
    char *end;
    if (s[0] == '\0' || isspace(s[0]))
        return STR2INT_INCONVERTIBLE;
    errno = 0;
    long l = strtol(s, &end, base);
    /* Both checks are needed because INT_MAX == LONG_MAX is possible. */
    if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
        return STR2INT_OVERFLOW;
    if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
        return STR2INT_UNDERFLOW;
    if (*end != '\0')
        return STR2INT_INCONVERTIBLE;
    *out = l;
    return STR2INT_SUCCESS;
}

int main(void) {
    int i;
    /* Lazy to calculate this size properly. */
    char s[256];

    /* Simple case. */
    assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
    assert(i == 11);

    /* Negative number . */
    assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
    assert(i == -11);

    /* Different base. */
    assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
    assert(i == 17);

    /* 0 */
    assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
    assert(i == 0);

    /* INT_MAX. */
    sprintf(s, "%d", INT_MAX);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MAX);

    /* INT_MIN. */
    sprintf(s, "%d", INT_MIN);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MIN);

    /* Leading and trailing space. */
    assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);

    /* Trash characters. */
    assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);

    /* int overflow.
     *
     * `if` needed to avoid undefined behaviour
     * on `INT_MAX + 1` if INT_MAX == LONG_MAX.
     */
    if (INT_MAX < LONG_MAX) {
        sprintf(s, "%ld", (long int)INT_MAX + 1L);
        assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
    }

    /* int underflow */
    if (LONG_MIN < INT_MIN) {
        sprintf(s, "%ld", (long int)INT_MIN - 1L);
        assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
    }

    /* long overflow */
    sprintf(s, "%ld0", LONG_MAX);
    assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);

    /* long underflow */
    sprintf(s, "%ld0", LONG_MIN);
    assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);

    return EXIT_SUCCESS;
}

GitHub 上游.

基于:https ://stackoverflow.com/a/6154614/895245

于 2012-10-16T21:46:10.320 回答
30

不要使用ato...组中的函数。这些已损坏且几乎无用。一个适度更好的解决方案是使用sscanf,尽管它也不是完美的。

要将字符串转换为整数,strto...应使用组中的函数。在您的特定情况下,它将strtol起作用。

于 2011-08-11T06:34:53.260 回答
9

您可以atoi()为乐趣编写代码:

int my_getnbr(char *str)
{
  int result;
  int puiss;

  result = 0;
  puiss = 1;
  while (('-' == (*str)) || ((*str) == '+'))
  {
      if (*str == '-')
        puiss = puiss * -1;
      str++;
  }
  while ((*str >= '0') && (*str <= '9'))
  {
      result = (result * 10) + ((*str) - '0');
      str++;
  }
  return (result * puiss);
}

你也可以让它递归,它可以折叠成 3 行。

于 2011-08-11T08:20:16.947 回答
4
int atoi(const char* str){
    int num = 0;
    int i = 0;
    bool isNegetive = false;
    if(str[i] == '-'){
        isNegetive = true;
        i++;
    }
    while (str[i] && (str[i] >= '0' && str[i] <= '9')){
        num = num * 10 + (str[i] - '0');
        i++;
    }
    if(isNegetive) num = -1 * num;
    return num;
}
于 2020-02-13T05:06:01.750 回答
3

只是想分享一个未签名的解决方案。

unsigned long ToUInt(char* str)
{
    unsigned long mult = 1;
    unsigned long re = 0;
    int len = strlen(str);
    for(int i = len -1 ; i >= 0 ; i--)
    {
        re = re + ((int)str[i] -48)*mult;
        mult = mult*10;
    }
    return re;
}
于 2016-10-18T19:26:26.200 回答
1

如前所述,atoi函数族不应在任何 C 程序中使用,因为它们没有任何错误处理。

strtol函数系列是 100% 等效的,但具有扩展功能:它具有错误处理功能,并且还支持十进制以外的其他基数,例如十六进制或二进制。因此正确答案是:使用strtol(家庭)。

如果您出于某种原因坚持自己手动推出此功能,您应该尝试做类似的事情strtol,以防除了可选的符号和数字之外还有其他符号。例如,我们想要转换较大字符串中的数字是很常见的。

带有错误处理支持的简单版本可能类似于下面的示例。此代码仅适用于以 10 为基数的十进制数字,但在其他方面的行为类似于strtol将可选指针设置为指向遇到的第一个无效符号(如果有)。另请注意,此代码不处理溢出。

#include <ctype.h>

long my_strtol (char* restrict src, char** endptr)
{
  long result=0;
  long sign=1;

  if(endptr != NULL) 
  {
    /* if input is ok and endptr is provided, 
       it will point at the beginning of the string */
    *endptr = src;
  }

  if(*src=='-')
  {
    sign = -1;
    src++;
  }

  for(; *src!='\0'; src++)
  {
    if(!isdigit(*src)) // error handling
    {
      if(endptr != NULL)
      {
        *endptr = src;
      }
      break;
    }
    result = result*10 + *src - '0';
  }

  return result * sign;
}

为了处理溢出,例如可以添加代码计数字符并检查它们是否永远不会超过 10,假设 32 位long可以是 max 2147483647, 10 位。

于 2021-12-13T14:43:09.830 回答
-1

这是使用霍纳规则完成这项工作的代码:

#include <stdio.h>
int main(void)
{
   char num[7], *p;
   int valn = 0, sign=-1;
   scanf("%s", num);
   sign = num[0]=='-'? -1:+1;
   p=num+(sign==-1);
   while(*p) valn=valn*10+(*p++-'0'); /* Horner */
   printf("%d\n", valn*sign);
   return 0;
}


% gcc str_to_int.c
% ./a.out
32
32
% ./a.out
-32
-32

代码极简;它不检查边界。

该解决方案的要点是 k 位数 A 0 A 1 ... A k是一个多项式,并且可以使用霍纳规则计算多项式的值。以 N 为底的数的值可以写为 A 0 *N k +A 1 *N k-1 +...+A k *N 0。这就是这条线的作用:

while(*p) valn=valn*10+(*p++-'0');
于 2021-12-13T13:30:16.853 回答
-1

好的,我遇到了同样的问题。我想出了这个解决方案。它对我来说效果最好。我确实尝试了 atoi() 但对我来说效果不佳。所以这是我的解决方案:

void splitInput(int arr[], int sizeArr, char num[])
{
    for(int i = 0; i < sizeArr; i++)
        // We are subtracting 48 because the numbers in ASCII starts at 48.
        arr[i] = (int)num[i] - 48;
}
于 2015-09-05T15:36:16.993 回答
-3

你总是可以自己动手!

#include <stdio.h>
#include <string.h>
#include <math.h>

int my_atoi(const char* snum)
{
    int idx, strIdx = 0, accum = 0, numIsNeg = 0;
    const unsigned int NUMLEN = (int)strlen(snum);

    /* Check if negative number and flag it. */
    if(snum[0] == 0x2d)
        numIsNeg = 1;

    for(idx = NUMLEN - 1; idx >= 0; idx--)
    {
        /* Only process numbers from 0 through 9. */
        if(snum[strIdx] >= 0x30 && snum[strIdx] <= 0x39)
            accum += (snum[strIdx] - 0x30) * pow(10, idx);

        strIdx++;
    }

    /* Check flag to see if originally passed -ve number and convert result if so. */
    if(!numIsNeg)
        return accum;
    else
        return accum * -1;
}

int main()
{
    /* Tests... */
    printf("Returned number is: %d\n", my_atoi("34574"));
    printf("Returned number is: %d\n", my_atoi("-23"));

    return 0;
}

这将做你想做的事而不会造成混乱。

于 2015-01-03T00:42:02.270 回答
-3

这个功能可以帮助你

int strtoint_n(char* str, int n)
{
    int sign = 1;
    int place = 1;
    int ret = 0;

    int i;
    for (i = n-1; i >= 0; i--, place *= 10)
    {
        int c = str[i];
        switch (c)
        {
            case '-':
                if (i == 0) sign = -1;
                else return -1;
                break;
            default:
                if (c >= '0' && c <= '9')   ret += (c - '0') * place;
                else return -1;
        }
    }

    return sign * ret;
}

int strtoint(char* str)
{
    char* temp = str;
    int n = 0;
    while (*temp != '\0')
    {
        n++;
        temp++;
    }
    return strtoint_n(str, n);
}

参考:http ://amscata.blogspot.com/2013/09/strnumstr-version-2.html

于 2013-09-12T11:31:53.193 回答
-3
//I think this way we could go :
int my_atoi(const char* snum)
{
 int nInt(0);
 int index(0);
 while(snum[index])
 {
    if(!nInt)
        nInt= ( (int) snum[index]) - 48;
    else
    {
        nInt = (nInt *= 10) + ((int) snum[index] - 48);
    }
    index++;
 }
 return(nInt);
}

int main()
{
    printf("Returned number is: %d\n", my_atoi("676987"));
    return 0;
}
于 2015-11-06T13:05:21.583 回答
-5

在 C++ 中,您可以使用这样的函数:

template <typename T>
T to(const std::string & s)
{
    std::istringstream stm(s);
    T result;
    stm >> result;

    if(stm.tellg() != s.size())
        throw error;

    return result;
}

这可以帮助您将任何字符串转换为任何类型,例如 float、int、double ...

于 2011-08-11T06:41:55.173 回答
-8

是的,您可以直接存储整数:

int num = 45;

如果你必须解析一个字符串,atoi或者strol要赢得“最短代码量”竞赛。

于 2011-08-11T06:32:42.297 回答