Linux программирование в примерах - Арнольд Роббинс
Шрифт:
Интервал:
Закладка:
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 так, что оставшиеся аргументы представляют файлы для обработки.
53 if (optind == argc) /* файлов нет, по умолчанию stdin */
54 process("standard input", stdin);
55 else {
56 /* цикл с файлами */
57 for (i = optind; i < argc; i++) {
58 if (strcmp(argv[i], "-") == 0)
59 process("standard input", stdin);
60 else if ((fp = fopen(argv[i], "r")) != NULL) {
61 process(argv[i], fp);
62 fclose(fp);
63 } else {
64 fprintf(stderr, "%s: %s: could not open: %sn",
65 argv[0], argv[i], strerror(errno));
66 errors++;
67 }
68 }
69 }
70
71 regfree(&pattern);
72 return errors != 0;
73 }
Строки 53–69 обрабатывают файлы, отыскивая соответствующие шаблону строки. Строки 53–54 обрабатывают случай, когда файлы не указаны: программа читает со стандартного ввода. В противном случае, строки 57–68 обрабатывают в цикле файлы. Строка 58 обрабатывает особый случай '-', обозначающий стандартный ввод, строки 60–62 обрабатывают обычные файлы, а строки 63–67 обрабатывают ошибки.
75 /* compile_pattern --- компиляция шаблона */
76
77 void compile_pattern(const char *pat)
78 {
79 int flags = REG_NOSUB; /* информация о месте совпадения не требуется */
80 int ret;
81 #define MSGBUFSIZE 512 /* произвольно */
82 char error[MSGBUFSIZE];
83
84 if (ignore_case)
85 flags |= REG_ICASE;
86 if (extended)
87 flags |= REG_EXTENDED;
88
89 ret = regcomp(&pattern, pat, flags);
90 if (ret != 0) {
91 (void)regerror(ret, &pattern, error, sizeof error);
92 fprintf(stderr, "%s: pattern '%s': %sn", myname, pat, error);
93 errors++;
94 }
95 }
Строки 75–95 определяют функцию compile_pattern(). Она сначала устанавливает REG_NOSUB в flags, поскольку нам нужно знать лишь «подходит ли строка?», а не «где в строке располагается подходящий текст?»
Строки 84-85 добавляют дополнительные флаги в соответствии с опциями командной строки. Строка 89 компилирует шаблон, а строки 90–94 сообщают о возникших ошибках
97 /* process --- читает строки текста и сопоставляет их с шаблоном */
98
99 void process(const char *name, FILE *fp)
100 {
101 char *buf = NULL;
102 size_t size = 0;
103 char error[MSGBUFSIZE];
104 int ret;
105
106 while (getline(&buf, &size, fp) != -1) {
107 ret = regexec(&pattern, buf, 0, NULL, 0);
108 if (ret != 0) {
109 if (ret != REG_NOMATCH) {
110 (void)regerror(ret, &pattern, error, sizeof error);
111 fprintf(stderr, "%s: file %s: %sn", myname, name, error);
112 free(buf);
113 errors++;
114 return;
115 }
116 } else
117 printf("%s: %s", name, buf); /* вывести подходящие строки */
118 }
119 free(buf);
120 }
Строки 97–120 определяют функцию process(), которая читает файл и выполняет сопоставление с регулярным выражением. Внешний цикл (строки 106–119) читает строки ввода. Для избежания проблем с длиной строки мы используем getline() (см. раздел 3.2.1.9 «Только GLIBC: чтение целых строк: getline() и getdelim()»). Строка 107 вызывает regexec(). Ненулевое возвращаемое значение означает либо неудачное сопоставление, либо какую-нибудь другую ошибку. Строки 109–115 соответственно проверяют REG_NOMATCН и выводят ошибку лишь тогда, когда возникла какая-нибудь другая проблема — неудачное сопоставление не является ошибкой
Если возвращаемое значение равно 0, строка совпала с шаблоном и соответственно строка 117 выводит имя файла и совпавшую строку.
122 /* usage --- вывод сообщения об использовании и выход */
123
124 void usage(void)
125 {
126 fprintf(stderr, "usage: %s [-i] [-E] pattern [ files ... ]n", myname);
127 exit(1);
128 }
Функция usage() выводит сообщение об использовании и завершает программу. Она вызывается, когда предоставлены недействительные аргументы или не предоставлен шаблон (строки 38–40 и 44–45).
Вот и все! Скромная, но тем не менее полезная версия grep в 130 строк кода.
12.9. Рекомендуемая литература
1. Programming Pearls, 2nd edition, by Jon Louis Bentley Addison-Wesley, Reading, Massachusetts, USA, 2000. ISBN- 0-201-65788-0. См. также веб-сайт этой книги.[131]
Проектирование программы с операторами проверки является одной из главных тем в этой книге.
2. Building Secure Software How to Avoid Security Problems the Right Way, by John Viega and Gary McGraw Addison-Wesley, Reading, Massachusetts, USA, 2001. ISBN: 0-201-72152-X.
Состояния гонки являются одной из многих проблем, о которых нужно побеспокоиться при написании безопасного программного обеспечения. Другой проблемой являются случайные числа. Данная книга рассматривает наряду с другими обе эти проблемы (Мы упоминали о ней в предыдущей главе.)
3. The Art of Computer Programming. Volume 2. Seminumerical Algorithms, 3rd edition, by Donald E. Knuth Addison-Wesley, Reading, Massachusetts, USA, 1998. ISBN- 0-201-89684-2.[132] См также веб-сайт этой книги.[133]
Это классическое справочное руководство по генерации случайных чисел.
4. Random Number Generation and Monte Carlo Methods, 2nd edition, by James E. Gentle Springer-Verlag, Berlin, Germany. 2003. ISBN: 0-387-00178-6.
Данная книга широко освещает методы генерации и тестирования псевдослучайных чисел. Хотя для неё также требуется математическая и статистическая подготовка, уровень не такой высокий, как в книге Кнута. (Благодарим Nelson H.F. Beebe за указание этой ссылки.)
5. sed & awk, 2nd edition, by Dale Dougherty and Arnold Robbins. O'Reilly and Associates, Sebastopol, California, USA, 1997. ISBN: 1-56592-225-5.
Эта книга осторожно вводит в регулярные выражения и обработку текста, начиная с grep и продвигаясь к более мощным инструментам sed и awk.
6. Mastering Regular Expressions, 2nd edition, by Jeffrey E.F. Friedl. O'Reilly and Associates, Sebastopol, California, USA, 2002.[134] ISBN: 0-59600-289-0.
Регулярные выражения являются важной частью Unix. Чтобы научиться заменять, вырезать и распределять текст с использованием регулярных выражений, мы рекомендуем эту книгу
7. Руководство для GNU grep также объясняет регулярные выражения. На системе GNU/Linux для просмотра локальной копии вы можете использовать 'info grep'. Или использовать браузер для прочтения онлайн-документации проекта GNU для grep.[135]
12.10. Резюме
• Операторы проверки предоставляют способ сделать утверждения о предполагаемом состоянии программы. Они являются полезным инструментом для проектирования и отладки и обычно должны оставаться в коде изделия. Однако, будьте внимательны, чтобы не перепутать операторы проверки с проверками возможных ошибок времени исполнения.