从C中的不规则字符串中获取所有整数

时间:2011-06-07 21:00:56

标签: c parsing string ansi

我正在寻找一种(相对)简单的方法来解析随机字符串并从中提取所有整数并将它们放入数组中 - 这与其他一些类似的问题不同,因为我的字符串没有标准格式。

示例:

pt112parah salin10n m5:isstupid::42$%&%^*%7first3

我需要最终得到一个包含这些内容的数组:

112 10 5 42 7 3

我想要一种更有效的方法,然后通过字符串逐字逐句。

感谢您的帮助

6 个答案:

答案 0 :(得分:2)

快速解决方案。我假设没有数字超出long的范围,并且没有减号可以担心。如果这些是问题,那么您需要做更多工作来分析strtol()的结果,并且需要检测'-'后跟数字。

代码会遍历所有字符;我认为你不能避免这种情况。但它确实使用strtol()来处理每个数字序列(一旦找到第一个数字),并继续strtol()停止的地方(而strtol()很友好地告诉我们它在哪里停止转换。)

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

int main(void)
{
    const char data[] = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
    long results[100];
    int  nresult = 0;

    const char *s = data;
    char c;

    while ((c = *s++) != '\0')
    {
        if (isdigit(c))
        {
            char *end;
            results[nresult++] = strtol(s-1, &end, 10);
            s = end;
        }
    }

    for (int i = 0; i < nresult; i++)
        printf("%d: %ld\n", i, results[i]);
    return 0;
}

输出:

0: 112
1: 10
2: 5
3: 42
4: 7
5: 3

答案 1 :(得分:1)

因为我整天都在写Python而且我想休息一下。声明一个数组将是棘手的。您必须运行两次以计算出您拥有的数量(然后分配数组)或者只是逐个使用这些数字,如本示例所示。

注意,'0'到'9'的ASCII字符是48到57(即连续)。

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

int main(int argc, char **argv)
{
    char *input = "pt112par0ah salin10n m5:isstupid::42$%&%^*%7first3";

    int length = strlen(input);
    int value = 0;
    int i;
    bool gotnumber = false;
    for (i = 0; i < length; i++)
    {
        if (input[i] >= '0' && input[i] <= '9')
        {
            gotnumber = true;
            value = value * 10; // shift up a column
            value += input[i] - '0'; // casting the char to an int
        }
        else if (gotnumber) // we hit this the first time we encounter a non-number after we've had numbers
        {
            printf("Value: %d \n", value);
            value = 0;
            gotnumber = false;
        }
    }

    return 0;
}
编辑:以前的版本没有处理0

答案 2 :(得分:1)

更多效率比逐字逐句?

不可能,因为你必须查看每个角色才能知道它不是整数。

现在,考虑到你必须按字符逐个字符串,我建议简单地将每个字符转换为int并检查:

//string tmp = ""; declared outside of loop.
//pseudocode for inner loop:
int intVal = (int)c;
if(intVal >=48 && intVal <= 57){ //0-9 are 48-57 when char casted to int.
    tmp += c;
}
else if(tmp.length > 0){
    array[?] = (int)tmp; // ? is where to add the int to the array.
    tmp = "";
}

数组将包含您的解决方案。

答案 3 :(得分:0)

另一种解决方案是使用strtok函数

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," abcdefghijklmnopqrstuvwxyz:$%&^*");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " abcdefghijklmnopqrstuvwxyz:$%&^*");
  }
  return 0;
}

给出:

112
10
5
42
7
3

这可能不是此任务的最佳解决方案,因为您需要指定将被视为令牌的所有字符。但它可以替代其他解决方案。

答案 4 :(得分:0)

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

int main(void)
{
    char *input = "pt112par0ah salin10n m5:isstupid::42$%&%^*%7first3";
    char *pos = input;
    int integers[strlen(input) / 2];   // The maximum possible number of integers is half the length of the string, due to the smallest number of digits possible per integer being 1 and the smallest number of characters between two different integers also being 1
    unsigned int numInts= 0;

    while ((pos = strpbrk(pos, "0123456789")) != NULL) // strpbrk() prototype in string.h
    {
        sscanf(pos, "%u", &(integers[numInts]));

        if (integers[numInts] == 0)
            pos++;
        else
            pos += (int) log10(integers[numInts]) + 1;        // requires math.h

        numInts++;
    }

    for (int i = 0; i < numInts; i++)
        printf("%d ", integers[i]);

    return 0;
}

通过在偏移指针上重复调用strpbrk()来完成查找整数,指针再次偏移等于整数中的位数的量,通过查找基数为10的对数来计算。整数并加1(当整数为0时有特殊情况)。在计算对数时,不需要对整数使用abs(),正如您所说的那样,整数将是非负的。如果您希望更节省空间,可以使用unsigned char integers[]而不是int integers[],因为您声明整数将全部<256,但这不是必需的。

答案 5 :(得分:0)

如果您不介意使用C ++而不是C(通常没有很好的理由),那么您可以将解决方案简化为两行代码(使用AX解析器生成器):

vector<int> numbers;
auto number_rule = *(*(axe::r_any() - axe::r_num()) 
   & *axe::r_num() >> axe::e_push_back(numbers));

现在测试一下:

std::string str = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
number_rule(str.begin(), str.end());
std::for_each(numbers.begin(), numbers.end(), [](int i) { std::cout << "\ni=" << i; });

果然,你得到了你的号码。

作为奖励,在解析unicode宽字符串时你不需要改变任何东西:

std::wstring str = L"pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
number_rule(str.begin(), str.end());
std::for_each(numbers.begin(), numbers.end(), [](int i) { std::cout << "\ni=" << i; });

果然,你得到了相同的数字。