如何在C语言中从字符串中提取数字?

23

假设我有一个字符串ab234cid*(s349*(20kd,我想提取其中所有的数字234, 349, 20,应该怎么做?


5
(1) 你到目前为止尝试了什么? (2) 你是否熟悉isdigit()函数?它可以在此链接中找到:http://www.cplusplus.com/reference/clibrary/cctype/isdigit/ - WhozCraig
1
-123算作数字吗?+123呢? - Keith Thompson
7个回答

38

你可以使用strtol来完成它,就像这样:

char *str = "ab234cid*(s349*(20kd", *p = str;
while (*p) { // While there are more characters to process...
    if ( isdigit(*p) || ( (*p=='-'||*p=='+') && isdigit(*(p+1)) )) {
        // Found a number
        long val = strtol(p, &p, 10); // Read number
        printf("%ld\n", val); // and print it.
    } else {
        // Otherwise, move on to the next character.
        p++;
    }
}

链接至ideone


1
+1: 但是为什么要使用 long long val = strtol();?我宁愿使用 C99 的 strtoll() 或者将 val 定义为 long :) - pmg
2
@HappyGreenKidNaps 这就是strtol函数的第二个参数如此有用的原因。 - Sergey Kalinichenko
@dasblinkenlight -- 我并不经常从流中读取数字,但我看到的所有strto函数族的示例都使用不同的变量作为结束指针。他们在调用后检查errno,对返回值进行合理性检查以查看它是否在预期范围内,然后才覆盖字符串流的指针与结束指针。 - Happy Green Kid Naps
1
@HappyGreenKidNaps:如果调用不成功,因为没有数字存在,p的值将不会被调用更改(或者说,它将使用先前相同的值进行更新)。 - Stephen Canon
6
如果出现负数,比如说ab-234cid*,怎么办呢? - Raulp
显示剩余4条评论

17

使用sscanf()和扫描集的可能解决方案:

const char* s = "ab234cid*(s349*(20kd";
int i1, i2, i3;
if (3 == sscanf(s,
                "%*[^0123456789]%d%*[^0123456789]%d%*[^0123456789]%d",
                &i1,
                &i2,
                &i3))
{
    printf("%d %d %d\n", i1, i2, i3);
}

其中%*[^0123456789]表示忽略输入,直到找到数字。在http://ideone.com/2hB4UW上查看演示。

或者,如果数字数量未知,您可以使用%n说明符记录缓冲区中读取的最后位置:

const char* s = "ab234cid*(s349*(20kd";
int total_n = 0;
int n;
int i;
while (1 == sscanf(s + total_n, "%*[^0123456789]%d%n", &i, &n))
{
    total_n += n;
    printf("%d\n", i);
}

提供的字符串是任意的,这可能无法很好地处理随机数量的整数。不管怎样,谢谢! - CDT
@CDT,第二个代码片段执行了。 - hmjd
为什么对于输入234cid*(s349*(20kd不起作用? - Sathyamoorthy R
这对正数不起作用。 - Mattwmaster58

3

下面是一种使用sscanf的简单解决方案:

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

char str[256]="ab234cid*(s349*(20kd";
char tmp[256];

int main()
{

    int x;
    tmp[0]='\0';
    while (sscanf(str,"%[^0123456789]%s",tmp,str)>1||sscanf(str,"%d%s",&x,str))
    {
        if (tmp[0]=='\0')
        {
            printf("%d\r\n",x);
        }
        tmp[0]='\0';

    }
}

2
创建一个状态机,它的基本原则是:当前字符是否为数字。
  • 当从非数字状态转移到数字状态时,将当前数字初始化为 number。
  • 当从数字状态转移到数字状态时,"移位"新数字:
    current_number := current_number * 10 + number;
  • 当从数字状态转移到非数字状态时,输出当前数字。
  • 当从非数字状态转移到非数字状态时,不做任何操作。
优化是可能的。

1
我不熟悉状态机,但这给了我一些新的想法,非常感谢! - CDT

1
如果字符串中的数字由空格分隔,则可以使用sscanf()。但是,对于您的示例情况不是这种情况,您需要自己处理:
char tmp[256];

for(i=0;str[i];i++)
{
  j=0;
  while(str[i]>='0' && str[i]<='9')
  {
     tmp[j]=str[i];
     i++;
     j++;
  }
  tmp[j]=0;
  printf("%ld", strtol(tmp, &tmp, 10));
  // Or store in an integer array

}


1
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
void main(int argc,char *argv[])
{
char *str ="ab234cid*(s349*(20kd", *ptr = str;
while (*ptr) { // While there are more characters to process...
    if ( isdigit(*ptr) ) {
        // Found a number
        int val = (int)strtol(ptr,&ptr, 10); // Read number
        printf("%d\n", val); // and print it.
    } else {
        // Otherwise, move on to the next character.
        ptr++;
    }
}

}

欢迎来到Stack Overflow!虽然这段代码可能解决了问题,但包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您提出代码建议的原因。请尽量不要在代码中加入过多的解释性注释,这会降低代码和解释的可读性! - Filnor

0

或者你可以像这样编写一个简单的函数:

// Provided 'c' is only a numeric character
int parseInt (char c) {
    return c - '0';
}

如果你决定回答一个已经有了明确且正确答案的老问题,那么在后期添加新答案可能不会给你带来任何好处。 如果你有一些独特的新信息,或者你确信其他答案都是错误的,那么请添加一个新答案,但是“又一个答案”通常不会为你赢得太多信用,因为它只是在很长时间之后提供了相同基本信息的答案。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接