Использование регулярного выражения POSIX в C

На самом деле я пытаюсь создать свой собственный текстовый пользовательский интерфейс сервера (для управления FTP, SSH-соединением, диспетчером задач и т. д.). Моя проблема здесь в диспетчере задач

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

Year Month Day Week-Day Hour Min Second ; Command

Для простоты я использовал тот же процесс, что и cron, где * эквивалентно любому моменту соответствующая категория

* * * * 00 00 00 ; reboot //allow me to run reboot everyday at midnight

Для этого я решил использовать регулярное выражение POSIX. Я хочу, чтобы он форматировал:

YEAR [0-9] {1-9}
MONTH [0-9] {2}
DAY [0-9] {2}
WEEK-DAY [A-Z] [a-z] {3}
HOUR [0-9] {2}
MINUTE [0-9] {2}
SECOND [0-9] {2}

COMMAND can be any printable character

Это приводит меня к проблеме. Я смог создать это регулярное выражение:

char *regexString = "^(\\*|([[:digit:]]){1,9})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:alpha:]]){3})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]];[[:blank:]]([[:print:]])*";

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

Output :
Match 0, Group 0: [ 0-25]: * * * * 00 00 00 ; reboot
Match 0, Group 1: [ 0- 1]: *

Можете ли вы помочь мне понять? Спасибо (:

PS: Вот несколько примеров:

* * * * * * * ; command //Match
0 00 00 Mon 00 00 00 ; command //Match
123456789 00 00 Mon 00 00 00 ; command //Match

01234556789 00 00 Mon 00 00 00 ; command //Don't Match
0 00 00 0 00 00 00 ; command //Don't Match
0 0 0 Mon 0 0 0 ; command //Don't Match

EDIT: вот код, который я использую

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

int main ()
{
    char * source = "* * * * 00 00 00 ; reboot";
    char *regexString = "^(\\*|([[:digit:]]){1,9})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:alpha:]]){3})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]](\\*|([[:digit:]]){2})[[:blank:]];[[:blank:]]([[:print:]])*";
    size_t maxMatches = 3; //I've tried for sevrals values, 2, 3 ... same Output
    size_t maxGroups = 3; //I've tried for sevrals values, 2, 3 ... same Output

    regex_t regexCompiled;
    regmatch_t groupArray[maxGroups];
    unsigned int m;
    char * cursor;

    if (regcomp(&regexCompiled, regexString, REG_EXTENDED))
    {
        printf("Could not compile regular expression.\n");
        return 1;
    };

    m = 0;
    cursor = source;
    for (m = 0; m < maxMatches; m ++)
    {
        if (regexec(&regexCompiled, cursor, maxGroups, groupArray, 0))
            break;  // No more matches

        unsigned int g = 0;
        unsigned int offset = 0;
        for (g = 0; g < maxGroups; g++)
        {
            if (groupArray[g].rm_so == (size_t)-1)
                break;  // No more groups

            if (g == 0)
                offset = groupArray[g].rm_eo;

            char cursorCopy[strlen(cursor) + 1];
            strcpy(cursorCopy, cursor);
            cursorCopy[groupArray[g].rm_eo] = 0;
            printf("Match %u, Group %u: [%2u-%2u]: %s\n",
                   m, g, groupArray[g].rm_so, groupArray[g].rm_eo,
                   cursorCopy + groupArray[g].rm_so);
        }
        cursor += offset;
    }

    regfree(&regexCompiled);

    return 0;
}

Примеры выходов:

//Case of a match :
Output :
Match 0, Group 0: [ 0-25]: * * * * 00 00 00 ; reboot
Match 0, Group 1: [ 0- 1]: * // YEAR
Match 0, Group 2: [ 2- 3]: * // MONTH
Match 0, Group 3: [ 4- 5]: * // DAY
Match 0, Group 4: [ 6- 7]: * // WEEK-DAY
Match 0, Group 5: [ 8- 10]: 00 //HOUR
Match 0, Group 6: [ 11- 13]: 00 //MINUTE
Match 0, Group 7: [ 14- 16]: 00 // SECOND
Match 0, Group 8: [ 20- 25]: reboot //COMMAND
$> echo $?
0

//Case of a match :
Output :
Match 0, Group 0: [ 0-38]: 123456789 00 00 Mon 00 00 00 ; Command
Match 0, Group 1: [ 0- 9]: 123456789 //YEAR
Match 0, Group 2: [ 10- 12]: 00 //MONTH
Match 0, Group 3: [ 13- 15]: 00 //DAY 
Match 0, Group 4: [ 16- 19]: Mon //WEEK-DAY
Match 0, Group 5: [ 20- 22]: 00 //HOUR
Match 0, Group 6: [ 23- 25]: 00 //MINUTE
Match 0, Group 7: [ 26- 28]: 00 //SECOND
Match 0, Group 8: [ 31- 38]: Command //COMMAND
$> echo $?
0

//Case of Not Match
$> echo $?
0

person Dzious    schedule 17.10.2019    source источник
comment
какую библиотеку регулярных выражений вы используете?   -  person virolino    schedule 17.10.2019
comment
Что именно вам не понятно? Выход кажется разумным. Первая группа — это полное совпадение, за которой следует каждая группа в скобках. Обратите внимание, что в примере кода задается maxMatches = 2, который вы должны изменить, чтобы увидеть все совпадения.   -  person nwellnhof    schedule 17.10.2019
comment
@virolino regex.h базовая библиотека регулярных выражений C   -  person Dzious    schedule 17.10.2019
comment
Вы злоупотребляете группами захвата, удалите повторяющиеся. Добавьте $ в конце. ^(\*|[[:digit:]]{1,9})[[:blank:]](\*|[[:digit:]]{2})[[:blank:]](\*|[[:digit:]]{2})[[:blank:]](\*|[[:alpha:]]){3}[[:blank:]](\*|[[:digit:]]{2})[[:blank:]](\*|[[:digit:]]{2})[[:blank:]](\*|[[:digit:]]{2})[[:blank:]];[[:blank:]][[:print:]]*$. См. regex101.com/r/77fXWl/1. Теперь 1) опубликуйте код, который вы используете, 2) предоставьте точный вывод для каждого образца ввода. Непонятно, что вы делаете, потому что get each component и Match/Don't match подразумевают разные типы вывода.   -  person Wiktor Stribiżew    schedule 17.10.2019
comment
Вам нужно показать код уважения, компилирующий регулярное выражение и код, использующий его. Создайте минимально воспроизводимый пример.   -  person Jonathan Leffler    schedule 17.10.2019
comment
@nwellnhof Извините, я забыл об этом сказать, я изменил maxMatches, и каким бы ни был номер, у меня тот же результат. Я не понимаю, как я смогу выбрать каждую из моих групп в скобках.   -  person Dzious    schedule 17.10.2019
comment
@WiktorStribiżew Я добавил код, который я использовал, я также добавил некоторые выходные данные, которые я пытаюсь получить с помощью get each component, я имею в виду попытку получить год, месяц, ... Когда я говорю, что соответствует регулярному выражению, строка должна быть распознается как часть регулярного выражения. Я не могу поставить $ в конце, у меня, вероятно, будет \n в конце моей строки (до \0)   -  person Dzious    schedule 17.10.2019
comment
Достаточно ли добавить код, который я использовал @JonathanLeffler?   -  person Dzious    schedule 17.10.2019
comment
Это хорошо. Такие детали, как REG_EXTENDED, важны и ранее не упоминались в вопросе.   -  person Jonathan Leffler    schedule 17.10.2019


Ответы (1)


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

Вы должны избавиться от всех избыточных групп захвата и использовать

char *regexString = "^(\\*|[[:digit:]]{1,9})[[:blank:]](\\*|[[:digit:]]{2})[[:blank:]](\\*|[[:digit:]]{2})[[:blank:]](\\*|[[:alpha:]]{3})[[:blank:]](\\*|[[:digit:]]{2})[[:blank:]](\\*|[[:digit:]]{2})[[:blank:]](\\*|[[:digit:]]{2})[[:blank:]];[[:blank:]]([[:print:]]*)";

Регулярное выражение (см. демонстрацию) теперь имеет 8 групп захвата, поэтому установите значение maxGroups равным 9:

 size_t maxGroups = 9; // 8 groups + 1 for whole match

И ваш код должен работать, см. онлайн-демонстрацию.

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

person Wiktor Stribiżew    schedule 17.10.2019
comment
Спасибо ! На самом деле, я буду отправлять свою функцию только одну строку за другой, поэтому у меня не будет кратных совпадений. Но для понимания я использовал (и сохранил) этот код. Еще раз спасибо за вашу помощь и объяснения :) - person Dzious; 17.10.2019