Извлечение чисел из строки с помощью регулярных выражений - PullRequest
2 голосов
/ 16 марта 2020

Я пытаюсь извлечь число 4 и 3 из строки /ab/cd__my__sep__4__some__sep__3. Я пытаюсь с регулярным выражением, но не уверен, как бы я это сделал. Я написал следующий код, но он просто печатает __my__sep__4__some__sep__3

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

int main() {
    char* s = "/ab/cd__my__sep__4__some__sep__3";
    regex_t regex;
    int reti = regcomp(&regex,"__my__sep__([0-9]+)",REG_EXTENDED);
    if(reti!=0) {
        exit(-1);
    }else {
        regmatch_t match[2];
        reti = regexec(&regex, s, 2, match, 0);
        if(reti == 0) {
            char *v = &s[match[1].rm_so];
            ssize_t fl;
            sscanf(v, "%zu", &fl);
            printf("%s",v);
        }else {
            printf("else");
        }
    }
}

Как мне извлечь числа 4 и 3?

Ответы [ 3 ]

3 голосов
/ 16 марта 2020

match[0] относится к той части текста, которая соответствует всему шаблону. match[1] - это совпадение, соответствующее первому захвату (заключенный в скобки подшаблон).

Обратите внимание, что &s[match[1].rm_so] дает вам указатель на начало захвата, но если вы напечатаете строку в этой точке, вы будете получить часть строки, начиная с начала захвата. В этом случае это не имеет значения. Поскольку вы используете sscanf для извлечения целочисленного значения захваченного текста, тот факт, что подстрока не завершается сразу, не имеет значения; за ним не будет следовать ди git, и sscanf остановится на первом не-ди git.

Но в общем случае возможно, что это будет не так просто чтобы определить конец сопоставленного захвата, и вы можете использовать один из следующих методов:

Если вы хотите напечатать захват, вы можете использовать формат вычисленной ширины строки: (См. Примечание 1.)

printf("%.*s\n", match[1].rm_eo - match[1].rm_so, &s[match[1].rm_so]);

Если у вас есть strndup, вы можете легко создать динамически распределяемую копию захвата: (См. Примечание 2.)

char* capture = strndup(&s[match[1].rm_so], match[1].rm_eo - match[1].rm_so);

Как быстрый и грязный хак, также можно просто вставить терминатор NUL (при условии, что искомая строка не является неизменной, что означает, что она не может быть строковым литералом). Возможно, вы захотите сохранить старое значение следующего символа, чтобы вы могли восстановить строку в ее первоначальное состояние:

char* capture = &s[match[1].rm_so];
char* rest = &s[match[1].rm_eo];
char saved_char = *rest;
*rest = 0;
/* capture now points to a NUL-terminated string. */
/* ... */
/* restore s */
*rest = saved_char;

Ничто из вышеперечисленного не является действительно необходимым в контексте исходного вопроса, так как sscanf, как написано, будет отлично работать, если вы измените начало строки для сканирования с match[0] на match[1].

Примечания:

  1. In В общем случае вы должны проверить, чтобы убедиться, что захват действительно был найден, прежде чем пытаться использовать его смещение. Член rm_so будет равен -1, если перехват не был найден во время поиска по регулярному выражению. Это не обязательно означает, что поиск не удался, потому что перехват может быть частью альтернативы, не используемой в совпадении.

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

0 голосов
/ 16 марта 2020

Поскольку вы используете sscanf(), нет необходимости использовать регулярное выражение . Вы можете проанализировать два числа из вашей строки, используя только sscanf(), используя строку формата: "%*[^0-9]%d%*[^0-9]%d", где "%*[^0-9]" использует подавление присваивания '*' для чтения и отбрасывания всех не-di git символов, а затем использует "%d" чтобы извлечь целочисленное значение. Полная строка формата просто повторяет эти два шаблона дважды.

Краткий пример с использованием вашего ввода может быть:

#include <stdio.h>

int main (void) {

    char *s = "/ab/cd__my__sep__4__some__sep__3";
    int a, b;

    if (sscanf (s, "%*[^0-9]%d%*[^0-9]%d", &a, &b) == 2)
        printf ("a: %d\nb: %d\n", a, b);
    else {
        fputs ("error: parse of integers failed.\n", stderr);
        return 1;
    }
}

Пример использования / вывода

$ ./bin/parse2ints
a: 4
b: 3

Если вы обнаружите, что пытаетесь разобрать что-то, с чем sscanf() не может справиться, тогда регулярное выражение подходит. Здесь sscanf() более чем способен удовлетворить ваши потребности в одиночку.

0 голосов
/ 16 марта 2020

Создать формат регулярного выражения, который содержит только [0-9]. Затем создайте отдельную логическую функцию, проверяющую, принадлежит ли символ вашему регулярному выражению. Затем примените функцию к вашей строке. Если true, добавьте символ в строку, которую вы хотите вывести

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...