Поддержка регулярных выражений очень полезна в CGI-программировании.
В C++ отсутствуют встроенные механизмы для работы с ними. Разработчик,
однако, может воспользоваться библиотекой Perl Compatible Regular
Expressions (PCRE), ее исходные коды и документация находятся на сайте
http://www.pcre.org.
Установка
Дистрибутив содержит подробное описание процесса установки библиотеки
как в юникс-системах, так и под Windows. В последнем случае
потребуется предварительно немного изменить исходные файлы. После
компиляции появятся библиотеки pcre.lib и pcreposix.lib, которые
следует подключать к проектам, использующим PCRE.
Минимальная программа
Прежде всего нужно подключить заголовочный файл pcre.h.
#include <pcre.h>
Для простоты зафиксируем регулярное выражение (шаблон) и строку,
которая будет с ним сопоставлена, непосредственно в исходном тексте
программы в двух символьных массивах.
char pattern[] = "e";
char str[] = "test";
Если необходимо работать со строками на русском языке, можно
попытаться подключить соответствующую локаль. О том, как это сделать,
рассказано в статье "Локаль в PCRE".
Наконец, мы готовы скомпилировать шаблон. Для этого следует вызвать
функцию pcre_compile(). В первом аргументе нужно указать ссылку
на строку, содержащую регулярное выражение. Во втором параметре можно
указать различные атрибуты (соответствующие опциям /igms... в Perl).
Следующие два параметра возвращают расшифровку сообщения об ошибках
компиляции. В последнем аргументе ожидается NULL или ссылку на таблицу
локали. В случае успеха функция вернет указатель на структуру типа
pcre.
pcre *re;
int options = 0;
const char *error;
int erroffset;
re = pcre_compile ((char *) pattern, options, &error, &erroffset, NULL);
Примечание. Может оказаться так, что в поставку с компилятором уже
входит библиотека PCRE. Однако, конкретная реализация может отличаться
от оригинальной. В частности, некоторые популярные компиляторы
в определении функции pcre_exec() обходятся без параметра options.
Если при компиляции шаблона произошла ошибка, переменная re будет
содержать значение NULL.
if (!re){
cout << "Failedn";
}
Если ошибки нет, можно вызывать функцию pcre_exec() для поиска
совпадений. В первом аргументе передают значение, возвращенное функций
pcre_compile(). Следующие три аргумента -- анализируемая строка,
ее длинна и смещение, начиная с которого будет обрабатываться строка.
Далее идет параметр, указывающий опции (их подробное описание
приведено в документации). В последних двух аргументах нужно указать
ссылку на массив целых чисел и его длину.
else{
int count = 0;
int ovector[30];
count = pcre_exec (re, NULL, (char *) str, 4, 0, 0, ovector, 30);
После работы функция возвратит число совпадений или отрицательное
число в случае ошибки.
if (!count){
cout << "No matchn";
Если найдены совпадения, то в массиве ovector будут записаны пары
индексов, указывающих на начало и конец совпадений. Первые два
элемента массива описывают положение всего совпавшего выражения.
Остальные пары -- положение подстрок, которые совпали с выражениями
в круглых скобках в шаблоне (это аналоги переменных вида $1 в Perl).
Размер массива индексов произволен, но он должен быть кратен трем.
Длина определяет максимальное число строк, которые можно получить при
сопоставлении с шаблоном, если в нем встретятся подстроки в круглых
скобках.
В нашем примере (шаблон e и строка test) найдется совпадение со второй
буквой в строке. Поэтому первые два элемента массива ovector содержат
числа 1 и 2, то есть указывают на совпадение, начинающееся в первой
позиции строки и заканчивающееся во второй. Индексация элементов
в символьных массивах C++ начинается с нуля.
Если шаблон содержит строку (e), в массиве ovector значимыми станут
две пары индексов, причем в этом случае они совпадут, поскольку весь
шаблон полностью заключен в круглые скобки.
Рассмотрим еще один простой пример, записав регулярное выражение
e(.)t. Шаблон совпадет с подстрокой est, а в первую переменную попадет
подстрока s. Поскольку теперь совпадение начинается с первого символа,
а длина совпавшей части равна трем, в первых двух элементах массива
ovector появятся значения 1 и 4. Следующая пара -- 2 и 3 (то есть
подстрока единичной длины, начинающаяся во второй позиции образца).
Полный код
Код простейшей программы приведен ниже. Шаблон и строка зафиксированы
в ее теле. Программа выводит только пары индексов из массива ovector.
#include <iostream.h>
#include <pcre.h>
int main(){
char pattern[] = "e";
char str[] = "test";
const unsigned char *tables = NULL;
setlocale (LC_CTYPE, (const char *) "ru.");
tables = pcre_maketables();
pcre *re;
int options = 0;
const char *error;
int erroffset;
re = pcre_compile ((char *) pattern, options, &error, &erroffset, NULL);
if (!re){
cout << "Failedn";
}
else{
int count = 0;
int ovector[30];
count = pcre_exec (re, NULL, (char *) str, 4, 0, 0, ovector, 30);
if (!count){
cout << "No matchn";
}
else{
for (int c = 0; c < 2 * count; c += 2){
if (ovector[c] < 0){
cout << "<unset>n";
}
else{
cout << ovector[c] << "/" << ovector[c + 1] << "n";
}
}
}
}
return 0;
}
Как построить локаль для правильной работы функций PCRE.
В предыдущей статье было рассказано о том, как применять библиотеку
PCRE (Perl Compatible Regular Expressions) в программах
на C++. Здесь же рассказано, как организовать работу библиотеки
с текстами на русском языке. Одна из первоочередных задач --
обеспечить правильную работу функций с игнорированием регистра
символов.
Установка локали
Если обратиться к определению функции pcre_compile(), то следует
обратить внимание на последний аргумент tableptr:
pcre* pcre_compile (const char *pattern,
int options,
const char **errptr,
int *erroffset,
const unsigned char *tableptr);
Если при вызове этой функции установить последний аргумент в NULL,
библиотека воспользуется собственной таблицей pcre_default_tables,
которая определена в файле chartables.c, в свою очередь
вкомпилированный в библиотечный модуль pcre.lib.
Настроить локаль для работы с национальным алфавитом можно как минимум
двумя способами. Во-первых, можно исправить упомянутую таблицу
до компиляции библиотеки. Второй способ более универсален, его
и рассмотрим.
Сформировать правильную таблицу локали помогает функция
pcre_maketables(). Она не требует передачи аргументов,
но <<подсознательно>> учитывает текущую локаль. Поэтому, чтобы
построить необходимую таблицу, вначале следует установить локаль
обычными средствами, доступными в программе на C++, а затем вызвать
pcre_maketables() и сохранить возвращенный результат -- указатель
на таблицу локали. Этот указатель следует передать в последнем
аргументе при вызове функции pcre_compile():
Обратите внимание на то, что после того, как построена таблица,
функции библиотеки PCRE не учитывают текущую локаль, а работают только
с этой таблицей. Это свойство можно с успехом использовать, чтобы
не нарушать работу остальных функций, пользующихся локалью.
В следующем примере строится таблица необходимой локали, а затем
устанавливается локаль, действующая ранее: