LINUX.ORG.RU

pcre, можно ли получить опционально встречающиеся значения?


0

1

Есть некие фрагменты текста, которые, кроме общей обязательной части, могут содержать или не содержать строки с дополнительной информацией, то есть, к примеру, что-то такое:

заголовок
параметр 1: значение1
параметр 2: значение2

заголовок
параметр 2: значение2
параметр 3: значение3

Порядок строк постоянен, поэтому первое, что пришло в голову, это использовать конструкции вроде (?:параметр1: (\\d+))? - то есть строчка может присутствовать, и тогда нам требуется числовое значение, а может не присутствовать, тогда оно собственно и не нужно. Но такой подход не работает :-( Так нельзя?

Вот пример кода:

#include <iostream>
#include <string>
#include <pcrecpp.h>

using namespace std;
using namespace pcrecpp;

int main(int argc, char *argv[])
{
    string text = "Это тест\n"
                  "первое значение: 10\n"
                  "второе значение: 20\n"
                  ;

    RE re("Это тест\n"
          "(?:первое значение: (\\d+)\n)?"
          "(?:второе значение: (\\d+)\n)?"
          , RE_Options(PCRE_UTF8));

    int v1 = -1, v2 = -1;
    bool res = re.FullMatch(text, &v1, &v2);
    cout << res << " " << v1 << " " << v2 << endl;
    return 0;
}

Если код в таком виде, то re.FullMatch(text, &v1, &v2) возвращает true. Если в тексте закомментировать «первое значение: 10\n» или «второе значение: 20\n», то re.FullMatch(text, &v1, &v2) возвращает false (причём если закомментировать «второе значение: 20\n», то переменная v1 всё-таки меняет своё значение, но v2 - нет, ну а если закомментировать «первое значение: 10\n», то обе переменные сохраняют значение -1).

Но вот если не пытаться получить значения из скобок, то есть вместо re.FullMatch(text, &v1, &v2) написать re.FullMatch(text), то результат во всех трёх случаях - true. То есть получается, что само регулярное выражение срабатывает как нужно, дело лишь в том, что FullMatch может не хватать результатов чтобы записать в заданные переменные.

Похоже нельзя обработать такой текст одним регулярным выражением, нужно анализировать каждую строку отдельно? Или может всё-таки есть возможность не усложнять код и использовать одно регулярное выражение?

★★★★

Use lex/yacc, Luke

R:
<Section> ::= <Hdr> <Params> | <Hdr>
<Hdr> ::= общая_часть_ну_ты_понял
<Params> ::= <Param> <Value>
<Param> ::= параметр_1 | параметр_2 | нутыпонял
<Value> ::= строка_число_или_чтотамутебя_или_вообще_пустота

yaws ()
Ответ на: комментарий от yaws

Ok, спасибо, посмотрю. Хотя я несколько упростил задачу, сам текст не имеет чёткой структуры, это лог, в котором встречаются записи, имеющие некоторую структуру.

askh ★★★★ ()

Не силён в этих выражениях, но, как я понимаю, если скобки нет, то её и нет. То бишь, надо, как минимум, брать в скобки «первое значение», «второе значение» и проверять, какие и сколько реально встречаются. Кстати, а почему int v1? По идее, сопоставление с regexp должно бы давать текстовые отрезки, переводить в число — твоя задача...

fat-II ()
Ответ на: комментарий от fat-II

С int v1 всё в порядке, используя эту библиотеку числовые значения можно получать как число.

А возвращаясь к регулярным выражениям, в выражении

(?:первое значение: (\\d+)\n)?

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

askh ★★★★ ()
Ответ на: комментарий от askh

Так и не работает вход, шоб его.

Так вот: могу ошибаться, но если (?:первое значение: (\\d+)\n)? в строке отсутствует, то и соответствующее (\\d+) отсутствует — не пустая строка, а именно отсутствие. То бишь, чтобы по найденным подстрокам различить, что там есть, чего нет, надо, например, сделать так: (?:(первое значение:) (\\d+)\n)?. Тогда в списке подстрок будет что-то типа «второе значение:» 10 «четвёртое значение» 11, а не просто 10 11 безо всякой информации, к каким параметрам они относятся

fat-ii

anonymous ()
Ответ на: комментарий от anonymous

Ну без чего-то такого как раз и хотелось обойтись, то есть чтобы одним регулярным выражением - раз, и всё обработать, установить все имеющиеся переменные. Судя по процитированному ниже фрагменту man pcrecpp, так можно сделать, если использовать строки, а не числа:

CAVEAT: An optional sub-pattern that does  not  exist  in  the  matched string  is  assigned  the  empty  string. Therefore, the following will return false (because the empty string is not a valid number):
int number;
pcrecpp::RE::FullMatch("abc", "[a-z]+(\\d+)?", &number);
askh ★★★★ ()
Ответ на: комментарий от askh

Может, чего-нить типа (?первое значение:(\\d+)|()) и строковыми переменными. При любом варианте одна скобка таки сопоставляется.

fat-II ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.