Ниже представлен пример типичного вызова функции.
if ((rerr = regcomp(&p, "(^(.*[^\])#.*$)|(^[^#]+$)",
REG_EXTENDED|REG_NEWLINE))) {
if (rerr == REG_NOMATCH) {
/* строка просто не совпадает с регулярным выражением */
} else {
/* какая-то другая ошибка, например, неправильно сформированное регулярное выражение */
}
}
Данное расширенное регулярное выражение находит строки в файле, которые не включены в комментарии, или которые, по крайней мере, частично, заключены в комментарии посредством символов # без префикса . Эту разновидность регулярного выражения удобно использовать в качестве простого анализатора синтаксиса для конфигурационного файла какого-нибудь приложения.
Даже если вы компилируете выражение, которое, по вашему мнению, является нормальным, вам все равно необходимо проверить его на наличие ошибок. Функция regcomp() возвращает нулевое значение при успешном выполнении компиляции и ненулевой код ошибки — в противном случае. Большинство ошибок может быть связано с разного рода ошибками в регулярных выражениях, но не исключено, что ошибка может быть связана с переполнением памяти. Далее в этой главе дается описание функции regerror().
#include <regex.h>
int regexec(const regex_t *preg, const chat *string, size_t nmatch,
regmatch_t pmatch[], int eflags);
Функция regexec() сравнивает строку с предварительно компилированным регулярным выражением. Аргумент eflags может иметь нулевое значение или быть любой комбинацией перечисленных ниже значений, объединенных битовым "ИЛИ".
REG_NOTBOL Первый символ строки не будет соответствовать символу ^. Любой символ, следующий за символом новой строки, будет соответствовать при том условии, что в вызове функции regcomp() будет задано значение REG_NEWLINE. REG_NOTEOL Последний символ строки не будет соответствовать символу $. Любой символ, предшествующий символу новой строки, будет соответствовать символу $ при том условии, что в вызове функции regcomp() будет задано значение REG_NEWLINE.
Массив структур regmatch_t используется для представления местоположения подвыражений в регулярном выражении.
#include <regex.h>
typedef struct {
regoff_t rm_so; /* индекс байта в строке в начале сопоставления*/
regoff_t rm_eo; /* индекс байта в строке в конце сопоставления*/
} regmatch_t;
Первый элемент regmatch_t описывает всю совпавшую строку; обратите внимание, что вся эта строка содержит любой символ начала строки, включая хвостовой символ новой строки, независимо от того, задано ли значение REG_NEWLINE.
Следующие элементы массива хранят подвыражения, заключенные в скобки, в том порядке, в котором они присутствуют в регулярном выражении, в порядке расположения открывающих скобок. (В коде на языке С элемент i эквивалентен выражению замены i в программах sed или awk.) В несовпадающих подвыражениях член regmatch_t.rm_so имеет значение -1.
В следующем коде производится сопоставление строки с регулярным выражением, содержащим подвыражения. После сопоставления на экран выводятся все совпавшие подвыражения.
1: /* match.с */
2:
3: #include <alloca.h>
4: #include <sys/types.h>
5: #include <regex.h>
6: #include <stdlib.h>
7: #include <string.h>
8: #include <stdio.h>
9:
10: void do_regerror(int errcode, const regex_t *preg) {
11: char *errbuf;
12: size_t errbuf_size;
13:
14: errbuf_size = regerror(errcode, preg, NULL, 0);
15: errbuf = alloca(errbuf_size);
16: if (!errbuf) {
17: perror("alloca");
18: return;
19: }
20:
21: regerror(errcode, preg, errbuf, errbuf_size);
22: fprintf(stderr, "%sn", errbuf);
23: }
24:
25: int main() {
26:
27: regex_t p;
28: regmatch_t *pmatch;
29: int rerr;
30: char *regex = "(^(.*[^\])#.*$)|(^[^#]+$)";
31: char string[BUFSIZ+1];
32: int i;
33:
34: if ((rerr = regcomp(&p, regex, REG_EXTENDED | REG_NEWLINE))) {
35: do_regerror(rerr, &p);
36: }
37:
38: pmatch = alloca(sizeof(regmatch_t) * (p.re_nsub+1));
39: if (!pmatch) {
40: perror("alloca");
41: }
42:
43: printf("Введите строку: ");
44: fgets(string, sizeof(string), stdin);
45:
46: if ((rerr = regexec(&p, string, p.re_nsub+1, pmatch, 0))) {
47: if (rerr == REG_NOMATCH) {
48: /* эту ситуацию может обработать regerror,
49: * но зачастую она обрабатывается особым образом
50: */
51: printf("Строка не совпадает с %sn", regex);
52: } else {
53: do_regerror(rerr, &p);
54: }
55: } else {
56: /* сопоставление закончено */
57: printf("Строка совпадает с регулярным выражением %sn", regex);
58: for (i = 0; i <= p.re_nsub; i++) {
59: /* вывод на экран совпавшей части (частей) строки */
60: if (pmatch[i].rm_so != -1) {
61: char *submatch;
62: size_t matchlen = pmatch[i].rm_eo - pmatch[i].rm_so;
63: submatch = malloc(matchlen+1);
64: strncpy(submatch, string+pmatch[i].rm_so,
65: matchlen);
66: submatch[matchlen] = ' ';
67: printf("совпавшее подвыражение %d: %sn", i,
68: submatch);
69: free(submatch);
70: } else {
71: printf ("нет совпадения с подвыражением %dn", i);
72: }
73: }
74: }
75: exit(0);
76: }
В примере регулярного выражения из программы match.с имеется три подвыражения. Первое из них представляет собой всю строку, содержащую текст, за которым следует символ комментария, вторым является текст в строке, предшествующей символу комментария, а третье представляет всю строку без символа комментария. Для строки, в начале которой содержится комментарий, элементу rm_so во втором и третьем элементе из массива pmatch[] присвоено значение -1. Для строки, в начале которой содержится комментарий, значение -1 присваивается первому и второму элементу; для строки, не содержащей символы комментария, второму и третьему элементу присваивается значение -1.
Каждый раз после завершения работы с компилированным регулярным выражением его необходимо освободить, чтобы избежать утечек памяти. Для освобождения памяти необходимо использовать функцию regfree(), но не free():
#include <regex.h>
void regfree(regex_t *preg);
В стандарте POSIX четко не сказано, следует ли использовать функцию regfree() каждый раз при вызове функции regcomp(), или же только после того, как вы в последний раз вызывали функцию regcomp() в одной структуре regex_t. Таким образом, чтобы избежать утечек памяти, в промежутках между использованием структур regex_t необходимо вызывать функцию regfree().
Всякий раз когда функция regcomp() или regex() возвращает ненулевой результат, функция regerror() может предоставить подробное сообщение, в котором будет указано, в чем состоит ошибка. Она записывает по возможности все сообщение об ошибке в буфер и возвращает размер всего сообщения. Поскольку вы заранее не знаете, какой размер будет иметь сообщение, то сначала вам необходимо узнать его размер, а затем распределить и использовать буфер, как показано в следующем далее примере кода. Поскольку этот вариант обработки ошибок быстро становится устаревшим, и вам придется включать его как минимум дважды (один раз после функции regcomp() и один раз после функции regex()), мы советуем вам написать код собственной оболочки функции regerror(), как показано в строке 10 из листинга math.с.
23.2.3. Простая утилита grep
grep является популярной утилитой, определенной в стандарте POSIX, которая предлагает возможности поиска регулярного выражения в текстовых файлах. Ниже показана простая (не соответствующая стандарту POSIX) версия утилиты grep, реализованная с помощью функций стандартного регулярного выражения.
1: /* grep.с */
2:
3: #include <alloca.h>
4: #include <ctype.h>
5: #include <popt.h>
6: #include <regex.h>
7: #include <stdio.h>
8: #include <string.h>
9: #include <unistd.h>
10: