Получить все целые числа из нерегулярных строк в C - PullRequest
1 голос
/ 08 июня 2011

Я ищу (относительно) простой способ разобрать случайную строку, извлечь из нее все целые числа и поместить их в массив - это отличается от некоторых других вопросов, которые похожи, потому что мои строки не имеют стандарта формат.

Пример:

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

Мне нужно в конечном итоге получить массив с таким содержимым:

112 10 5 42 7 3

И я хотел бы, чтобы метод был более эффективным, чем проход за символом через строку.

Спасибо за вашу помощь

Ответы [ 6 ]

2 голосов
/ 08 июня 2011

Быстрое решение.Я предполагаю, что нет чисел, которые превышают диапазон 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 голос
/ 08 июня 2011

Больше эффективнее , чем проходить символ за символом?

Невозможно, потому что вы должны смотреть на каждый символ, чтобы знать, что это не целое число.

Сейчас, учитывая, что вам нужно идти через строку символ за символом, я бы рекомендовал просто приводить каждый символ как целое и проверять, что:

//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 = "";
}

массив будет содержать ваше решение.

1 голос
/ 08 июня 2011

Просто потому, что я пишу на Python весь день, и я хочу отдохнуть.Объявление массива будет сложно.Либо вам нужно запустить его дважды, чтобы выяснить, сколько у вас чисел (а затем выделить массив), либо просто использовать числа один за другим, как в этом примере.

NB символы ASCII для '0' в«9» - от 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

0 голосов
/ 08 июня 2011

И если вы не возражаете против использования 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; });

и, конечно же, вы вернули те же цифры.

0 голосов
/ 08 июня 2011
#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, но это не обязательно. </p>

0 голосов
/ 08 июня 2011

Другое решение - использовать функцию 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

Возможно, не лучшее решение для этой задачи, поскольку вам необходимо указать все символы, которые будут обрабатываться как токен. Но это альтернатива другим решениям.

...