Работа с регулярными выражениями в C/C++ при помощи библиотеки libpcre

1 июня 2017

Регулярные выражения могут быть чрезвычайно полезны при решении множества задач. Пару лет назад в этом блоге рассматривались регулярные выражения C++11 (std::regex). Однако тогда они показали себя не очень хорошо (стоит отметить, что ситуация уже могла измениться к лучшему), да и в чистом C этими регулярками не воспользуешься. Поэтому в данном посте мы познакомимся с более консервативным, зато проверенным временем и гарантированно работающим подходом, заключающимся в использовании библиотеки libpcre.

Примечание: Вас также могут заинтересовать посты, посвященные библиотекам libcurl, zlib, libpq и libpcap.

Библиотека PCRE (расшифровывается, как Perl Compatible Regular Expressions) легко добавляется к проекту, если вы используете CMake:

# ...

find_library(PCRE_LIBRARY pcre)

# ...

target_link_libraries(main ${PCRE_LIBRARY})

Пример компиляции регулярного выражения:

#include <pcre.h>

/* ... */

const char* error;
int erroroffset;
char req_pattern[] = "^(GET|POST) ([^ ?]+)[^ ]* HTTP/1.[01]$";

pcre* req_re = pcre_compile(req_pattern, 0,
                            &error, &erroroffset, nullptr);
if(req_re == nullptr)
    throw std::runtime_error("PCRE compilation failed");

Скомпилированное регулярное выражение рано или поздно нужно освободить. В C/C++ на границе взаимодействия с библиотеками на чистом C для решения этой проблемы я предпочитаю использовать deferxx. Вы можете помнить эту библиотеку по заметке OpenGL: управление камерой при помощи мыши и клавиатуры. Библиотека добавляет в язык defer, который, аналогично defer в языке Go, предназначен для освобождения ресурсов при выходе из скоупа:

#include <defer.h>

/* ... */

defer( pcre_free(req_re) );

Для сопоставления строки с регулярным выражением предназначена процедура pcre_exec:

int mvector[32];

/* ... */

int rc = pcre_exec(req_re, nullptr, buf, strlen(buf), 0, 0,
                   mvector, sizeof(mvector)/sizeof(mvector[0]));
if(rc < 0)
    throw std::runtime_error("No match");

Если строка соответствует регулярному выражению, в mvector[0] и mvector[1] будут записаны индексы первого и последнего символа совпавшей части строки, в mvector[2] и mvector[3] — первое совпадение в скобочках, и так далее. Так в приведенном примере для вывода query string придется написать код вроде следующего:

int qstart = mvector[4];
int qend = mvector[5];
buf[qend] = '\0';
printQueryString(&buf[qstart]);

Заметьте, что последняя 1/3 буфера mvector используется процедурой pcre_exec для хранения временных данных. Это следует учитывать при расчете размера буфера. Например, буфера из 4-х элементов будет недостаточно, если в вашем регулярном выражении используется одна пара скобочек. На мой взгляд, это далеко не самый удачный API. Однако на практике можно не париться, и просто делать буфер побольше.

Собственно, это все! Полноценный пример использования libpcre вы найдете в этом репозитории. По крайней мере, он там был в районе коммита ed77154d. В будущем ситуация может немного измениться.

А какую библиотеку для работы с регулярными выражениями вы используете при программировании на C/C++?

Дополнение: Пример кэширования регулярных выражений можно найти в заметке Практическая польза классов-синглтонов с примером на C++.

Метки: .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.