POSIX-совместимые функции
Описания данных функции содержатся в заголовочном
файле /usr/include/preposix.h и библиотеке
/usr/lib/preposix.a. Они поддерживают малое количество
возможностей и созданы лишь для совместимости со стандартными.
Вот их список:
int regcomp(regex_t *preg, const char *pattern, int cflags);
int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
void regfree(regex_t *preg);
Функция regcomp предназначена для
преобразования регулярного выражения во внутреннюю форму.
Первый параметр (regex_t *preg) - это переменная, в которую заносится преобразованный
шаблон, который затем используется в функции regexec. Вторым
параметром (const char *pattern) передается преобразовываемое
регулярное выражение. Последний параметр (int cflags) содержит
флаги, задающие параметры преобразования:
- REG_ICASE
Указывает, что сравнение будет производится
без учета регистра символов.
- REG_NEWLINE
Указывает, что сравнение будет производится
в многострочной строке, т.е. содержащей '\n'.
В случае успешного преобразования функция regcomp
возвращает 0, иначе код ошибки.
Коды ошибок могут иметь следующие значения:
- REG_BADRPT
Неправильное использование модификатора,
такого как `*' в качестве первого символа.
- REG_BADBR
Неправильное использование оператора обратной
ссылки.
- REG_EBRACE
Незакрытая фигурная скобка в операторе
интервала.
- REG_EBRACK
Незакрытая скобка в операторе списка.
- REG_ERANGE
Неправильное использование оператора диапазона
- REG_ECTYPE
Неизвестный класс символа.
- REG_ECOLLATE
Неправильный символ шаблона.
- REG_EPAREN
Незакрытая скобка в операторе группировки.
- REG_ESUBREG
Неправильная обратная ссылка.
- REG_EEND
Неизвестная ошибка.
- REG_EESCAPE
Конечный backslash.
- REG_BADPAT
Неправильное использование таких операторов
шаблона как группирование и список.
- REG_ESIZE
Преобразованное регулярное выражение требует
буффер больше, чем 64K.
- REG_ESPACE
Не хватает памяти.
Функция regexec производит сравнение строки с
преобразованным регулярным выражением.
Первый параметр (regex_t *preg) - это переменная, в которой
содержится преобразованный функцией regcomp
шаблон. Второй параметр (const char *string) - строка, которая
Последний параметр (int eflags) содержит
флаги, задающие параметры поиска:
- REG_NOTBOL
Запрещает использование символа-шаблона начала
строки (^) в регулярном выражении. Поиск по таким шаблонам
всегда дает отрицательный результат.
- REG_NOTEOL
Запрещает использование символа-шаблона конца
строки ($) в регулярном выражении. Поиск по таким шаблонам
всегда дает отрицательный результат.
Третий (size_t nmatch) и четвертый (regmatch_t pmatch[]) параметры функции regexec
содержат результаты успешного поиска. Параметр pmatch
является массивом переменных, содержащих структуру, описанную следующим
образом:
typedef struct
{
regoff_t rm_so;
regoff_t rm_eo;
}
regmatch_t;
Элемент rm_so содержит номер символа,
первого в подстроке, соответствующей шаблону. rm_eo
содержит номер символа, последнего в подстроке, соответствующей
шаблону.
Параметр nmatch задает максимальное
количество элементов массива pmatch.
В случае успеха (нахождения подстроки,
соответствующей регулярному выражению) функция regexec
возвращает 0. В противном случает - REG_NOMATCH.
Рассмотрим простейший пример поиска по шаблону.
#include <stdio.h>
#include <pcreposix.h>
int main(int argc, char* argv[])
{
char *str="Hello123!"; /* строка, в которой будет производится поиск */
char *pattern="\\w{3}\\d+"; /* шаблон, по которому будет производится поиск */
regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
regex_t f; /* переменная, для хранения преобразованного шаблона */
int i,j;
if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
{
printf("Ошибка в шаблоне! %i\n",i);
exit(1);
}
if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
{
printf("Не найдено!\n");
exit(1);
}
printf("Найдено:\n");
/* номер первого и последнего символов найденной подстроки, соответствующей шаблону, */
/* заносятся в первый элемент массива p */
for(j=p[0].rm_so;j<p[0].rm_eo;j++)
putchar(str[j]);
putchar('\n');
return 0;
}
Собирается программа таким образом:
gcc -o имя_файла имя_файла.c `pcre-config --libs-posix`
Например, если файл исходника называется
example1.c, то собираться он будет при помощи команды:
gcc -o example1 example1.c `pcre-config --libs-posix`
В результате работы программы будет выведено:
Найдено:
llo123
Как указывалось в предущей главе, шаблоны можно
группировать. Делается при помощи скобок. В предыдущем примере мы
искали подстроку, состоящую из трех символов и и трех цифр. Предположим
тебе надо найти подстроку в строке, состоящую из какого-то количество букв и
некоторого количество цифр, причем нужно отделить буквы от цифр.
#include <stdio.h>
#include <pcreposix.h>
int main(int argc, char* argv[])
{
char *str="Hello123!"; /* строка, в которой будет производится поиск */
char *pattern="([A-Za-z]+)(\\d+)"; /* шаблон, по которому будет производится поиск */
regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
regex_t f; /* переменная, для хранения преобразованного шаблона */
int i,j;
if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
{
printf("Ошибка в шаблоне! %i\n",i);
exit(1);
}
if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
{
printf("Не найдено!\n");
exit(1);
}
printf("Найдено:\n");
/* номер первого и последнего символов найденной подстроки, соответствующей всему шаблону, */
/* заносятся в первый элемент массива p */
/* номера первого и последнего символов найденной подстроки, соответствующей частям шаблона, */
/* заключенных в скобки, попадают во второй и последующий элементы массива p */
/* остальные элементы массива p заполняются парами (-1,-1) */
for(i=0;p[i].rm_eo!=-1;i++)
{
for(j=p[i].rm_so;j<p[i].rm_eo;j++)
putchar(str[j]);
putchar('\n');
}
return 0;
}
В результате работы программы будет выведено:
Найдено:
Hello123
Hello
123
Давайте рассмотрим пример посложнее, типичный для
web-программирования - разбор URL'а на составные части -
протокол://имя_сервера/путь/имя_файла?параметры. Шаблон в таком случае будет
выглядеть таким образом: ^(\w+)://(.+?)/(.+)/(.+?)\?(.+?)$.
#include <stdio.h>
#include <pcreposix.h>
int main(int argc, char* argv[])
{
char *str="http://dh.opennet.ru/cgi-bin/dbi_mysql_stat.pl?start=1&ip=129.55.55.55";
char *pattern="^(\\w+)://(.+?)/(.+)/(.+?)\\?(.+?)$";
regmatch_t p[20];
regex_t f;
int i,j;
if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
{
printf("Ошибка в шаблоне!\n");
exit(1);
}
if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
{
printf("Не найдено!\n");
exit(1);
}
/* Первая группа скобок - протокол */
printf("Протокол: ");
for(j=p[1].rm_so;j<p[1].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Вторая группа скобок - сервер */
printf("Имя сервера: ");
for(j=p[2].rm_so;j<p[2].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Третья группа скобок - путь */
printf("Путь к файлу: ");
for(j=p[3].rm_so;j<p[3].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Четвертая группа скобок - файл */
printf("Имя файла: ");
for(j=p[4].rm_so;j<p[4].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Пятая группа скобок - параметры */
printf("Параметры: ");
for(j=p[5].rm_so;j<p[5].rm_eo;j++)
putchar(str[j]);
putchar('\n');
return 0;
}
Программа должна вывести:
Протокол: http
Имя сервера: dh.opennet.ru
Путь к файлу: cgi-bin
Имя файла: dbi_mysql_stat.pl
Параметры: start=1&ip=129.55.55.55
У предыдущего примера есть недостаток - в URL'е
необязательными элементами являются: путь, имя файла, параметры.
Любой из перечисленных элементов может отсутствовать. Предыдущий шаблон
требует иметь эти элементы в строке URL'а. Надо создать шаблон, который
будет предусмативать отсутсвие данных элементов. Сделать можно с
помощью такой конструкции - (шаблон)?, которая указывает, что
подстрока, соответствующая указанному шаблону может встречаться один
раз или отсутствовать. Выбраным нами условиям удовлетворяет такой
шаблон: ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$. Разберем
его по составным частям. Составные части буду выделять цветом. В первой группе -
^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - шаблон, под
который должен попадать протокол. Предполагается, что он должен указан
обязательно. Во второй группе - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - имя сервера. Также
обязательный параметр. Третья группа - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$
- путь, имя файла, параметры. Вся эта группа может присутствовать или
отсутствовать. Третья группа состоит из более мелких групп, первая из
которых ( и четвертая в общем контексте ) - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - указывает
на путь к файлу с завершающим "слэшем", который может также
присутствовать или отсутствовать. В четвертую группу входит пятая -
^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - обязательная для четвертой группы, т.е. если подстрока,
соответствующая четвертой группе присутствует (т.е. путь+/ ), значит
обязательно должна присутствовать пятая группа (т.е. просто путь).
Вторая группа в третьей группе /* я уже сам начинаю запутываться в
нумерации, представляю, как ты себя сейчас чувствуешь...:) */ или шестая
в общем контексте - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - указывает на
необязательный элемент имя файла+парметры. Шестая группа также состоит
из подгрупп /* никто и не обещал, что будет легко и безболезненно
разобраться с регулярными выражениями...:)) */ - самого имени файла (седьмая
группа - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$), необязательного для шестой группы, и символа "?"+параметры
(восьмая группа - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$), также необязательного для шестой группы.
Восьмая группа имеет внутри себя еще одну группу (девятую -
^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$. Наверное, можно это сделать более
понятно и коротко, но я выбрал такой шаблон, чтобы объяснить тебе, как
работает группировка. Надеюсь, мне это удалось. А вот и программа:
#include <stdio.h>
#include <pcreposix.h>
int main(int argc, char* argv[])
{
char *str1="http://dh.opennet.ru/cgi-bin/dbi_mysql_stat.pl?start=1&ip=129.55.55.55";
char *str2="http://dh.opennet.ru";
char *str3="http://dh.opennet.ru/files/aboutyou.tar.gz";
char *str4="http://dh.opennet.ru/main.html";
char *str=str4; /* здесь можно поменять на str1,str2 или str3. Поэкспериментировать..:) */
char *pattern="^(\\w+)://(.+?)(/((.+)/)?((.+?)?(\\?(.+?))?)?)?$";
regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
regex_t f; /* переменная, для хранения преобразованного шаблона */
int i,j;
if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
{
printf("Ошибка в шаблоне!\n");
exit(1);
}
if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
{
printf("Не найдено!\n");
exit(1);
}
/* Первая группа скобок - протокол */
printf("Протокол: ");
for(j=p[1].rm_so;j<p[1].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Вторая группа скобок - сервер */
printf("Имя сервера: ");
for(j=p[2].rm_so;j<p[2].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Третья группа скобок - путь+файл+параметры */
printf("Путь+файл+параметры: ");
for(j=p[3].rm_so;j<p[3].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Четвертая группа скобок - путь+/ */
printf("Путь+/: ");
for(j=p[4].rm_so;j<p[4].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Пятая группа скобок - путь */
printf("Путь к файлу: ");
for(j=p[5].rm_so;j<p[5].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Шестая группа скобок - имя файла+параметры */
printf("Имя файла+параметры: ");
for(j=p[6].rm_so;j<p[6].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Седьмая группа скобок - имя файла */
printf("Имя файла: ");
for(j=p[7].rm_so;j<p[7].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Восьмая группа скобок - ?+параметры */
printf("?+параметры: ");
for(j=p[8].rm_so;j<p[8].rm_eo;j++)
putchar(str[j]);
putchar('\n');
/* Девятая группа скобок - параметры */
printf("Параметры: ");
for(j=p[9].rm_so;j<p[9].rm_eo;j++)
putchar(str[j]);
putchar('\n');
return 0;
}
А вот и вывод:
Протокол: http
Имя сервера: dh.opennet.ru
Путь+файл+параметры: /main.html
Путь+/:
Путь к файлу:
Имя файла+параметры: main.html
Имя файла: main.html
?+параметры:
Параметры:
Пришло время рассказать о двух оставшихся функциях
- regerror и regfree.
Функция regerror преобразует код ошибки,
возвращаемый функциями regcomp и regexec, в сообщение об
ошибке. Первым параметром (int errcode) передается код ошибки.
Второй параметр (const regex_t *preg) - преобразованный шаблон.
Третий (char *errbuf) - строка, в которую будет записано
сообщение. Четвертый (size_t errbuf_size) - максимальная длина
строки. Возьмем самый первый пример в этой главе, добавив к нему
обработку ошибок и изменив немного шаблон так, чтобы в нем была ошибка,
например - незакрытая скобка.
#include <stdio.h>
#include <pcreposix.h>
int main(int argc, char* argv[])
{
char *str="Hello123!"; /* строка, в которой будет производится поиск */
char *pattern="(\\w{8}\\d+"; /* шаблон с ошибкой */
regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
regex_t f; /* переменная, для хранения преобразованного шаблона */
int error; /* код ошибки */
char erbuf[256]; /* сообщение об ошибке */
int i,j;
if(error=regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
{
regerror(error,&f,erbuf,256);
printf("Ошибка в шаблоне: %s\n",erbuf);
exit(1);
}
if(error=regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
{
regerror(error,&f,erbuf,256);
printf("Не найдено! %s\n",erbuf);
exit(1);
}
printf("Найдено:\n");
for(j=p[0].rm_so;j<p[0].rm_eo;j++)
putchar(str[j]);
putchar('\n');
return 0;
}
Вывод:
Ошибка в шаблоне: unbalanced () at offset 9
Функция regfree служит для освобождения
(очистки) переменной для хранения преобразованного шаблона, которая
передается как параметр (regex_t *preg). Это полезно, когда ты
хочешь использовать одну и ту же переменную при нескольких операциях
поиска. Например, когда ты выводишь все совпадения с указанным шаблоном
в одной строке.
#include <stdio.h>
#include <pcreposix.h>
int main(int argc, char* argv[])
{
char *str="Hello123!";
char *str_tmp=str;
char *pattern=".{3}";
regmatch_t p[20];
regex_t f;
int error;
char erbuf[256];
int i,j;
while(2*2==4)
{
if(error=regcomp(&f,pattern,0))
{
regerror(error,&f,erbuf,256);
printf("Ошибка в шаблоне: %s\n",erbuf);
exit(1);
}
if(error=regexec(&f,str_tmp,20,p,0))
{
regerror(error,&f,erbuf,256);
exit(1);
}
printf("Найдено:\n");
for(j=p[0].rm_so;j<p[0].rm_eo;j++)
putchar(str_tmp[j]);
putchar('\n');
regfree(&f);
str_tmp+=p[0].rm_eo;
}
return 0;
}
Вывод:
Найдено:
Hel
Найдено:
lo1
Найдено:
23!
Попробуй закомментировать строчку с regfree и
посмотри, что будет выведено.:)
Вроде все рассказал о POSIX-совместимых функциях.
Теперь перейдем к собственным функциям библиотеки PCRE.