[ Content | View menu ]

Построение регулярного выражения по списку строк

Опубликовано 18.08.2009

Имеется неколько десятков однотипных файлов вида FILE20090801011253.txt, FILE20090801023619.txt и т.д. Требуется составить регулярное выражение, которому удовлетворяют только названия файлов из списка.

Вручную это можно сделать примерно так:

FILE200908010(11253|23619)\.txt

Если файлов много, то никаких нервов не хватит высчитывать, проверять и перепроверять.

То же самое можно сделать полуавтоматически, с помощью механизма complete-into-braces оболочки bash. Сочетание клавиш Esc-{ преобразовывает список подстановки в более-менее оптимальный формат brace-completion, пригодный для дальнейшей обработки:

$ echo /path/to/dir/(Esc-{)FILE200908010{11253.txt,23619.txt}

Чтобы из получившейся строки сделать нормальный регэксп, нужно фигурные скобки заменить на круглые, а запятые — на вертикальную черту:

echo "FILE200908010{11253.txt,23619.txt} "|sed -e 's/{/(/g' -e 's/}/)/g' -e 's/,/|/g'

Приходится делать много лишних слабоавтоматизируемых действий, после чего копировать, вставлять, заменять и исправлять, что не очень удобно.

После непродолжительных поисков был найден Perl’овый модуль Regexp::List, функцию list2re (преобразование списка в регулярное выражение) из которого можно приспособить под любые подобные задачи. Вот, например, сокращенная версия скрипта для моего случая:

use Regexp::List;
my $l=Regexp::List->new;
$l->set(lookahead=>0);

opendir(D,"/path/to/dir") or die "Could not open $dir: $!";
my @list=grep {$_!~/^\.\.?$/ } readdir(D);          # get directory entries except "." and ".."
closedir(D);

my $re = $l->list2re(@list);                        # create regexp from @list
$re=~s/^\(\?-[xism]+:(.*?)\)$/^$1\$/g;              # strip "(?:-xism" and ")"
print "$re\n";

Для следующих десяти файлов:

FILE20090802120343.txt
FILE20090802165139.txt
FILE20090802181550.txt
FILE20090804014529.txt
FILE20090804140848.txt
FILE20090805103525.txt
FILE20090805104025.txt
FILE20090810083211.txt
FILE20090810120349.txt
FILE20090810121250.txt

Скрипт выдает такой результат:

^FILE200908(?:0(?:510(?:40|35)25\.txt|21(?:81550|20343|65139)\.txt|4(?:140848|014529)\.txt)|10(?:12(?:1250|0349)\.txt|083211\.txt))$

Что и требовалось.

Пользуясь случаем, хочу порекламировать PDF-версию третьего издания книги «Регулярные выражения» Джеффри Фридла
«
»

8 комментариев

Write a comment - TrackBack - RSS Comments

  1. Comment by adw0rd:

    Расскажите как мне использовать регулярки в консоли, например для проставления хозяина на скрытые фалйы:

    # chown -R www:www .*

    Тогда под шаблон попадает и «..», что нам не хотелось бы вовсе…
    Я думаю шаблон должен быть следующего вида:

    # chown -R www:www [^\.]\.*

    И еще я юзаю csh, если bash это умеет делать, то скажите, попробую его наконец-то.

    19.08.2009 @ 14:41
  2. Comment by bappoy:

    Например, так:

    $ touch 1 .2
    $ ls -a
    .  ..  1  .2
    $ echo *
    1
    $ shopt -s dotglob
    $ echo *
    1 .2

    Или так:

    find . -mindepth 1 -exec chown www:www {} \;
    19.08.2009 @ 15:02
  3. Comment by adw0rd:

    А нормальной поддержки регулярных выражение для файловых масок нет?

    Нормально это:
    # chown -R www:www [^\.]\.*

    Использование find+xargs я считаю костылем, потому что нет нормальной поддержки регулярок для файловых масок.

    19.08.2009 @ 16:18
  4. Comment by bappoy:

    Не костыль — это dotglob, аналогов которому в csh я навскидку не нашел.

    А несовпадение dotfiles с шаблоном *, равно как и совпадение папок . и .. с шаблоном .* обосновано стандартом POSIX Patterns Used for Filename Expansion, поэтому приходится извращаться разными способами.

    Расширенная поддержка регэкспов в globbing может усугубить несовместимость шеллов со стандартами, поэтому ее и не торопятся вводить. Хотя в третьем bash уже имеются кое-какие расширения в globbing в эту сторону.

    19.08.2009 @ 16:54
  5. Comment by adw0rd:

    Ясно, спасибо!

    20.08.2009 @ 09:29
  6. Comment by sdfsd:

    А для чего эта строка?
    $re=~s/^\(\?-[xism]+:(.*?)\)$/^$1\$/g;

    29.08.2009 @ 14:44
  7. Comment by bappoy:

    Regexp::List добавляет в начало каждой группы немного дополнительных модификаторов, в результате получается (?-xism:regexp). Поскольку мне эти регулярки нужны для использования не в перл, то я от них избавляюсь.

    29.08.2009 @ 18:17
  8. Comment by zx7cfx:

    За ‘Esc-{‘ отдельное спасибо!

    01.03.2016 @ 14:38
Write comment

Я не робот.