Linux программирование в примерах
Шрифт:
regoff_t rm_eo; /* Смещение первого байта после вложенной строки */
/* ...здесь возможный внутренний материал... */
} regmatch_t;
Как поле
re_nsub
, так и структура regmatch_t
предназначены для сопоставления вложенных выражений. Рассмотрим такое регулярное выражение:
[:пробел:]]+([[:цифра:]]+)[[:пробел:]]+([[:буква:]])+
Каждое из двух вложенных выражений в скобках могут соответствовать одному или более символам. Более того, текст, соответствующий каждому вложенному выражению, может начинаться и заканчиваться в произвольных участках строки.
regcomp
устанавливает в поле re_nsub
число вложенных выражений в скобках внутри регулярного выражения, regexec
pmatch
структур regmatch_t
смещениями начальных и конечных байтов текста, соответствующих этим вложенным выражениям. Вместе эти данные позволяют заменять текст — удалять его или заменять другим текстом, точно так же, как в текстовом редакторе pmatch[0]
описывает часть строки, соответствующую всему регулярному выражению. Участок от pmatch[1]
до pmatch[preg->re_nsub]
описывает ту часть, которая соответствует каждому вложенному выражению в скобках. (Таким образом, вложенные выражения нумеруются начиная с 1.) Элементы rm_so
и rm_eo
не используемых элементов массива pmatch
установлены в -1. regexec
заполняет не более nmatch-1
элементов pmatch
; поэтому следует убедиться, что имеется по крайней мере на 1 элемент больше, чем в preg->re_nsub
. Наконец, флаг
REG_NOSUB
для regcomp
означает, что начальная и завершающая информация не нужна. Этот флаг следует использовать, когда эти сведения не нужны; это потенциально может довольно значительно повысить производительность regexec
. Другими словами, если все, что вам нужно знать, это «соответствует ли?», включите
REG_NOSUB
. Однако, если нужно также знать, «где находится соответствующий текст?», этот флаг следует опустить. В заключение, как
regcomp
, так и regexec
возвращают 0, если они успешны, или определенный код ошибки, если нет. Коды ошибок перечислены в табл. 12.9. Таблица 12.9. Коды ошибок
regcomp
и regexec
Константа | Значение |
---|---|
REG_BADBR | Содержимое ' \{...\} ' недействительно. |
REG_BADPAT | Регулярное выражение недействительно |
REG_BADRPT | Символу ? , + или * не предшествует действительное регулярное выражение. |
REG_EBRACE | Фигурные скобки (' \{...\} ') не сбалансированы |
REG_EBRACK | Квадратные скобки (' [...] ') не сбалансированы |
REG_ECOLLATE | В шаблоне использован недействительный элемент сортировки |
REG_ECTYPE | В шаблоне использован недействительный класс символов |
REG_EESCAPE | В шаблоне есть завершающий символ \ |
REG_EPAREN | Группирующие скобки (' (...) ' или '\(...\) ') не сбалансированы |
REG_ERANGE | Конечная точка в диапазоне не действительна |
REG_ESPACE | Функции не хватило памяти |
REG_ESUBREG | Цифра в ' \цифра ' недействительна |
REG_NOMATCH | Строка не соответствует шаблону |
Для демонстрации регулярных выражений
ch12-grep.c
предусматривает базовую реализацию стандартной программы grep
, которая отыскивает соответствие шаблону. Наша версия использует по умолчанию базовые регулярные выражения. Для использования вместо этого расширенных регулярных выражений она принимает опцию – E
, а для игнорирования регистра символов опцию – i
. Как и настоящая grep
, если в командной строке не указаны файлы, наша grep
читает со стандартного ввода, а для обозначения стандартного ввода, как и в настоящей grep
, может быть использовано имя файла '–
'. (Эта методика полезна для поиска в стандартном вводе наряду с другими файлами.) Вот программа:
1 /* ch12-grep.c - Простая версия grep, использующая функции POSIX */
2
3 #define _GNU_SOURCE 1 /* для getline)) */
4 #include <stdio.h>
5 #include <errno.h>
6 #include <regex.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9
10 char *myname; /* для сообщений об ошибках */
11 int ignore_case = 0; /* опция -i: игнорировать регистр */
12 int extended = 0; /* опция -E: использовать расширенные регулярные выражения */
13 int errors = 0; /* число ошибок */
14
15 regex_t pattern; /* шаблон для поиска */
16
17 void compile_pattern(const char *pat);
18 void process(const char *name, FILE *fp);
19 void usage(void);
Строки 10–15 объявляют глобальные переменные программы. Первый набор (строки 10–13) для опций и сообщений об ошибках. Строка 15 объявляет
pattern
, в которой хранится откомпилированный шаблон. Строки 17–19 объявляют другие функции программы.
21 /* main --- обработка опций, открывание файлов */
22
23 int main(int argc, char **argv)
24 {
25 int с;
26 int i;
27 FILE *fp;
28
29 myname = argv[0];
30 while ((c = getopt(argc, argv, ":iE")) != -1) {
31 switch (c) {
32 case 'i':
33 ignore_case = 1;
34 break;
35 case 'E':
36 extended = 1;
37 break;
38 case '?':
39 usage;
40 break;
41 }
42 }
43
44 if (optind == argc) /* проверка исправности */
45 usage;
46
47 compile_pattern(argv[optind]); /* компилировать шаблон */
48 if (errors) /* ошибка компиляции */
49 return 1;
50 else
51 optind++;
В строке 29 устанавливается значение
myname
, а строки 30–45 анализируют опции. Строки 47–51 компилируют регулярное выражение, помещая результаты в pattern
, compilе_раttern
увеличивает значение errors
, если была проблема. (Соединение функций посредством глобальной переменной, как здесь, обычно считается плохой манерой. Для небольших программ, подобным этой, это сойдет, но для более крупных программ такое сопряжение может стать проблемой.) Если не было ошибок, строка 51 увеличивает значение optind
так, что оставшиеся аргументы представляют файлы для обработки.
Поделиться с друзьями: