Книга: Linux программирование в примерах

12.7.1. Простое сопоставление с шаблоном: fnmatch()

12.7.1. Простое сопоставление с шаблоном: fnmatch()

Мы начинаем с функции fnmatch() («filename match» сопоставление имени файла»).

#include <fnmatch.h> /* POSIX */
int fnmatch(const char *pattern, const char *string, int flags);

Эта функция сопоставляет string с pattern, который является обычным шаблоном групповых символов оболочки. Значение флагов (которое вскоре будет описано) изменяет поведение функции. Возвращаемое значение равно 0, если string соответствует pattern, FNM_NOMATCH, если не соответствует, и ненулевое значение, если возникла ошибка. К сожалению, POSIX не определяет каких-либо специфических ошибок; соответственно, вы можете лишь сказать, что что-то пошло не так, но не можете сказать, что.

Переменная flags является побитовым ИЛИ одного или более флагов, перечисленных в табл. 12.1.

Таблица 12.1. Значения флагов для fnmatch()

Флаг Только GLIBC Значение
FNM_CASEFOLD ? Сопоставление с учетом регистра
FNM_FILE_NAME ? Синоним GNU для FNM_PATHNAME
FNM_LEADING_DIR ? Флаг для внутреннего использования GLIBC; не используйте его в своих программах. Подробности см. в fnmatch(3)
FNM_NOESCAPE Обратный слеш является обычным символом, а не знаком перехода
FNM_PATHNAME Слеш в string должен соответствовать слешу в pattern, он не может быть подставлен через *, ? или '[...]'
FNM_PERIOD Начальная точка в string подходит, лишь если в pattern также есть начальная точка. Точка должна быть первым символом в string. Однако, если также установлен FNM_PATHNAME, точка, которая идет за слешем, также рассматривается как начальная

fnmatch() работает со строками из любого источника; сопоставляемые строки не обязательно должны быть действительными именами файлов. Хотя на практике fnmatch() используется в коде, читающем каталог с помощью readdir() (см раздел 5.3.1 «Базовое чтение каталогов»):

struct dirent dp;
DIR *dir;
char pattern[100];
/* ...заполнить шаблон, открыть каталог, проверить ошибки... */
while ((dp = readdir(dir)) != NULL) {
 if (fnmatch(pattern, dir->d_name, FNM_PERIOD) == 0)
  /* имя файла соответствует шаблону */
 else
  continue; /* не соответствует */
}

GNU ls использует fnmatch() для реализации своей опции --ignore. Вы можете предоставить несколько игнорируемых шаблонов (с помощью нескольких опций). ls сопоставляет каждое имя файла со всеми шаблонами. Она делает это с помощью функции file_interesting() в ls.с:

2269 /* Возвращает не ноль, если файл в 'next' должен быть перечислен. */
2270
2271 static int
2272 file_interesting(const struct dirent *next)
2273 {
2274  register struct ignore_pattern* ignore;
2275
2276  for (ignore = ignore_patterns; ignore; ignore = ignore->next)
2277   if (fnmatch(ignore->pattern, next->d_name, FNM_PERIOD) == 0)
2278    return 0;
2279
2280  if (really_all_files
2281   || next->d_name[0] !=
2282   || (all_files

2283   && next->d_name[1] != ' '
2284   && (next->d_name[1] || next->d_name[2] != '')))
2285   return 1;
2286
2287  return 0;
2288 }

Цикл в строках 2276–2278 сопоставляет имя файла со списком шаблонов для игнорируемых файлов. Если один из шаблонов подходит, файл не интересен и file_interesting() возвращает false (то есть 0).

Переменная all_files соответствует опции , которая показывает файлы, имена которых начинаются с точки, но не являются '.' и '..'. Переменная really_all_files соответствует опции , которая предполагает , а также показывает '.' и '..'. При наличии таких сведений, условие в строках 228–2284 может быть представлено следующим псевдокодом:

if (/* показать все файлы независимо от их имени (-а) */
 OR /* первый символ имени не точка */
 OR (/* показать файлы с точкой (-А) */
  AND /* в имени файла несколько символов */
  AND (/* второй символ не точка */
   OR /* третий символ не завершает имя */)))
 return TRUE;

ЗАМЕЧАНИЕ. fnmatch() может оказаться дорогостоящей функцией, если она используется в локали с многобайтным набором символов. Обсудим многобайтные наборы символов в разделе 13.4 «Можете произнести это для меня по буквам?»

Оглавление книги


Генерация: 2.085. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз