Использование UNIX: Часть 9. Регулярные выражения

Поиск и обработка текста

Практически все нетривиальные проблемы требуют отфильтровывать полезные данные от бесполезных. Многие утилиты командной строки UNIX® используют регулярные выражения для определения критериев обработки.

Мартин Стрейчер, главный редактор, Linux Magazine

Мартин Стрейчер (Martin Streicher) -- главный редактор журнала Linux Magazine. Он имеет степень магистра компьютерных наук Университета Пардью (Purdue University) и с 1982 занимается программированием на языках Pascal, C, Perl, Java и (с недавнего времени) Ruby в UNIX-подобных операционных системах.



14.03.2008

Дик печатает. Джейн вычисляет

Набираемую в командной строке UNIX команду можно представить как предложение:

  • выполняемые файлы, такие как cat и ls, - это глаголы, действия;
  • данные, выводимые на экран в результате работы программы, - это существительные, данные, которые должны быть внимательно изучены или использованы;
  • операторы командной оболочки, такие как | (конвейер) или > (перенаправление стандартного вывода), - это союзы, соединяющие простые предложения.

Например, команда ls -A | wc -l, которая подсчитывает число файлов и каталогов в текущем каталоге (исключая специальные объекты . и ..), состоит из двух предложений. Первое предложение ls -A - это глагол, перечисляющий содержимое текущей папки; второе предложение wc -l - это другой глагол, считающий строки. Первое предложение производит данные, которые используются вторым, а союз - конвейер - объединяет оба предложения.

Большинство команд, описанных в предыдущих статья, действуют таким же образом и имеют подобную предложению структуру.

Но без некоторого грамматического соуса, команда становится похожей на что-то типа "Дик печатает. Джейн вычисляет". Конечно, в этом примере первое предложение выполняет свою работу, но оно не так красиво, как текст "Гамлета". Решение многих сложных проблем требует использования прилагательных.

Практически все нетривиальные проблемы требуют выделять нужные данные. Число и вид атрибутов может меняться очень сильно, но с другой стороны, каждое решение (явно или неявно) описывает форму и структуру информации, которую оно ищет и обрабатывает, производя новую информацию в новой форме.

В командной строке регулярные выражения действуют как прилагательные - описания или уточнения. Когда они применяются к входным данным, регулярные выражения позволяют отличать полезные данные от бесполезных.

Небольшой урок пунктуации

Давайте рассмотрим следующий пример.

Утилита grep просматривает входной поток последовательно, строка за строкой, в поисках совпадений. В простейшем случае, grep выводит те строки, которые содержат текст, совпадающий с шаблоном. grep может находить фиксированные последовательности символов, даже игнорируя их регистр, для этого используется опция -i.

Таким образом, имея файл heroes.txt с такими строками:

Catwoman
Batman
The Tick
Spider Man
Black Cat
Batgirl
Danger Girl
Wonder Woman
Luke Cage
The Punisher
Ant Man
Dead Girl
Aquaman
SCUD
Spider Woman
Blackbolt
Martian Manhunter

следующая команда:

grep -i man heroes.txt

выдаст следующее:

Catwoman
Batman
Spider Man
Wonder Woman
Ant Man
Aquaman
Martian Manhunter

Здесь grep просматривает каждую строку файла heroes.txt и ищет букву m, за которой следует a, за которой, в свою очередь, следует n. Кроме отдельного слова, эти символы могут встречаться в других словах и быть частью большего слова. Catwoman, Batman, Spider Man, Wonder Woman, Ant Man, Aquaman и Martian Manhunter - каждая из этих строк содержит подстроку man(игнорируя регистр символов, о чем заботится опция -i).

Утилита grep содержит и другие опции, позволяющие увеличить ее возможности поиска. Например, опция -w ограничивает совпадения целыми словами, то есть вывод команды grep -i -w manне будет включать для нашего примера слова Catwoman и Batman.

Утилита также имеет хорошую возможность для исключения из вывода строк, содержащих шаблон. Используйте опцию -v для исключения строк, удовлетворяющих шаблону. Например:

grep -v -i 'spider' heroes.txt

выводит все строки за исключением тех, которые содержат подстроку spider:

Catwoman
Batman
The Tick
Black Cat
Batgirl
Danger Girl
Wonder Woman
Luke Cage
The Punisher
Ant Man
Dead Girl
Aquaman
SCUD
Blackbolt
Martian Manhunter

А что если необходимо найти только те имена, которые начинаются с "Bat"? Или слова, которые начинаются с "bat" "Bat" "cat" или "Cat?" Или необходимо узнать как много героических имен заканчивается на "man". В этих случаях простой поиск строки, как это делалось в трех предыдущих примерах, не поможет, так как утилита grep осуществляет поиск безотносительно к месту нахождения шаблона.

Поиск и выбор

Регулярные выражения могут фильтровать данные по положению, например, находящиеся в начале или конце строки, начале или конце слова. Регулярные выражения, обычно сокращенно называемые regex, могут описывать чередования (которые можно представить словами "этот" и "тот"), задавать фиксированное, переменное или бесконечное повторение, диапазоны (например, "любая буква от a до m"), характеризовать классы или виды символов ("печатаемые символы" или "знаки препинания"), а также много другое.

В таблице 1 собраны некоторые часто употребляемые операторы регулярных выражений. Можно создавать цепочку из примитивов, описанных в таблице 1 (и других операторов), и использовать их в комбинации для создания (очень) сложных регулярных выражений.

Таблица 1. Часто употребляемые операторы регулярных выражений
ОператорНазначение
. (точка)Любой одиночный символ.
^ (крышка)Пустая последовательность, возникающая в начале строки.
$ (знак доллара)Пустая последовательность, возникающая в конце строки.
AБуква A в верхнем регистре.
aБуква a в нижнем регистре.
\dОдиночная цифра.
\DОдиночный символ, не являющийся цифрой.
\wЛюбой одиночный цифро-буквенный символ, этот оператор - синоним [:alnum:].
[A-E]Любая буква из A, B, C, D или E в верхнем регистре.
[^A-E]Любой символ, кроме буквы из A, B, C, D или E в верхнем регистре.
X?Одна или ни одной заглавной буквы X.
X*Ноль или более заглавной буквы X.
X+Одна или более заглавная буква X.
X{n}Точно n заглавных букв X.
X{n,m}Не менее n и не более m заглавных букв X. Если опустить m, то оператор означает "как минимум n заглавных букв X".
(abc|def)+Не менее одной последовательности abc, def, abc и def будут считаться совпадением.

Далее я даю несколько примеров регулярных выражений, в которых используется утилита grep как средство поиска. Многие другие утилиты UNIX, включая интерактивные редакторы vi и Emacs, потоковые редакторы sed и awk и все современные языки программирования поддерживают использование регулярных выражений. Однажды изучив (считающийся загадочным) синтаксис регулярных выражений, можно применять свои знания в других программах, языках программирования и операционных системах.

Находим слова, начинающиеся с Bat

Чтобы найти строки, начинающиеся с Bat используйте следующую команду:

grep -E '^Bat'

Опция -E указывает, что используется регулярное выражение. ^ (крышка) означает начало строки - виртуальный символ, который является первым символом каждой строки. Буквы B, a и t означают, что должно быть найдено совпадение с этими символами. Следовательно, команда grep -E '^Bat' выдаст:

Batman
Batgirl

Так как многие из операторов регулярных выражений также используются командной оболочкой (некоторые в тех же целях, некоторые в иных), хорошей привычкой является заключать регулярное выражение, используемое в команде, в одинарные кавычки для защиты оператора от интерпретации командной оболочкой. Например, и * (звездочка), и $ (знак доллара) являются операторами регулярных выражений и используются командной оболочкой.

Ищем слова, заканчивающиеся на man

Чтобы найти слова, заканчивающиеся на man, необходимо написать такое регулярное выражение: man$, чтобы найти совпадение символов m, a и n, следующих в данном порядке перед знаком окончания строки, помеченным оператором регулярного выражения $.

Находим пустую строку

Используя знаки начала строки ^ и окончания строки $, легко найти пустую строку, используя регулярное выражение ^$ - строка, в которой символ окончания строки следует сразу же за символом ее начала.

Дизъюнкция или множества

Слова, начинающиеся с bat, Bat, cat или Cat, можно найти двумя способами. Первый способ - это применение оператора дизъюнкция, который означает совпадение, если любой из шаблонов в операторе совпадает с искомой строкой. Например, команда:

grep -E '^(bat|Bat|cat|Cat)' heroes.txt

делает следующее. Оператор регулярного выражения | (вертикальная черта) - это логический оператор "ИЛИ", то есть это | то дает совпадение со строкой это или со строкой то. Следовательно, ^(bat|Bat|Cat|cat) определяют "Начало строки, за которым следует bat, Bat, cat или Cat". Конечно, можно упростить регулярное выражение, используя grep -i (с этой опцией grep игнорирует регистр символов), и упростить команду до:

grep -i -E '^(bat|cat)' heroes.txt

Другой способ найти слова, начинающиеся с bat, Bat, cat или Cat - использование - использование [] (квадратных скобок) - множества. Если перечислить список символов в множестве, то появление любого из этих символов будет являться совпадением. (Можно считать множество упрощенным оператором "ИЛИ" для отдельных символов).

Например, команда:

grep -E '^[bcBC]at' heroes.txt

дает такой же результат, как и команда:

grep -E '^(bat|Bat|cat|Cat)' heroes.txt

Используя опцию -i также можно упростить свое регулярное выражение до ^[bc]at.

Больше того, можно включить диапазоны символов в множество с помощью оператора - (дефис). Например, имена пользователей обычно начинаются с буквы. Чтобы проверить правильность имени пользователя, например, которое с помощью Web-формы передается на сервер, можно использовать регулярное выражение вида ^[A-Za-z]. Это регулярное выражение ищет "Начало строки, за которым следует буква латинского алфавита в верхнем (A-Z) или нижнем (a-z) регистре". Кстати, [A-z] означает тоже самое, что и [A-Za-z].

Также можно перемежать диапазоны и отдельные символы в множестве. Регулярное выражение [A-MXYZ] ищет совпадение с символами латинского алфавита A - M, X, Y и Z (в верхнем регистре).

Наконец, если необходимо инвертировать множество (найти любые символы, кроме указанных в множестве), то можно использовать специальное множество [^ ] и набрать диапазон символов, которые не должны считаться совпадением. Ниже дан пример инвертированного множества. Чтобы найти всех супергероев с at в имени, исключив черного рыцаря Batman, наберите следующую команду:

grep -i -E '[^b]at' heroes.txt

Результат будет следующим:

Catwoman
Black Cat

Определенные множества используются в регулярных выражениях так часто, что для них были введены сокращения. Например, множество [A-z0-9_] может быть заменено оператором \w. В свою очередь, оператор \W означает множество [^A-z0-9_]. Также можно использовать запись [:alnum] вместо \w и [^[:alnum]] вместо \W.

Кстати, \w (и синоним [:alnum]) зависит от локали, в то время как [A-z0-9_] - это именно буквы A - z, цифры 0 - 9 и знак подчеркивания. При разработке интернационального приложения необходимо использовать зависящие от локали (locale) формы, чтобы сделать код портируемым на другие локали.

Повторение, повторение и еще раз повторение

Ранее мы узнали о буквенных, позиционных и двух видах операторов выбора. Используя их, можно создать шаблоны предсказуемой длины. Возвращаясь к именам пользователей, можно проверить, что каждое пользовательское имя начинается с буквы, за которой имеет ровно семь букв или цифр, используя следующее регулярное выражение:

[a-z][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]

Однако такой способ чересчур громоздкий. Кроме того, он подходит только для имен, содержащих ровно восемь символов. Этот способ не подходит для нахождения имен, содержащих от трех до восьми символов, которые также обычны для корректных имен пользователей.

Регулярные выражения могут включать модификаторы повторения. Модификатор повторения может означать число включений, равное а) нулю, одному или более; б) одному или более; в) от пяти до десяти; г) ровно трем. Модификаторы повторения должны быть объединены с другими шаблонами, они ничего не означают в отдельности.

Например, регулярное выражение

^[A-z][A-z0-9]{2,7}$

осуществляет фильтрацию имени пользователя с ограничениями, описанными раньше. Имя пользователя - это строка, начинающаяся с буквы, за которой следует как минимум две, но не более семи букв или чисел, и замыкаемая символом конца строки.

Операторы положения также необходимы здесь. Без двух этих операторов пользовательские имена произвольной длины будут ошибочно считаться корректными. Почему? Рассмотрим регулярное выражение:

^[A-z][A-z0-9]{2,7}

Оно задает вопрос: "Начинается ли строка с буквы, за которой следует от двух до семи букв или цифр?" Но оно не упоминает об условиях завершения строки. Таким образом, строка samuelclemens удовлетворит регулярному выражению, но очевидно слишком длинна, чтобы быть корректным именем пользователя. Также, если пропустить начинающий выражение оператор ^ или оба оператора положения, корректными будут признаны строки, оканчивающиеся или содержащие munster1313 соответственно. Если искомая строка должна быть определенной длины, то необходимо включить в регулярное выражение операторы начала и окончания строки.

Вот еще несколько примеров:

  • Можно использовать {2,}, чтобы найти два или более повторений. Регулярное выражение ^G[o]{2,}gle найдет Google, Gooogle, Goooogle и так далее.
  • Модификаторы повторения ?, + и * найдут ни одного или одно, одно или более, ноль и более вхождений шаблона соответственно. (Можно считать ? короткой записью выражения {0,1}).

    Регулярное выражение boys? найдет boy или boys; регулярное выражение Goo?gle найдет Gogle или Google.

    Регулярное выражение Goo+gle найдет Google, Gooogle и так далее.

    Конструкция Goo*gle найдет Gogle, Google, Gooogle и так далее.

  • Можно применять модификаторы повторения для отдельных букв, как это было показано только что, а также и для более сложных комбинаций. Используйте скобки ( и ) (как это делается в математике), чтобы применить модификатор для выражения. Вот еще один пример: имея следующий файл test.txt:
    The rain in Spain falls mainly 
    on the the plain.
    
    It was the best of of times;
    it was the worst of times.

    Команда grep -i -E '(\b(of|the)\W+){2,}' test.txt выведет:

    on the the plain.
    It was the best of of times;
  • Оператор регулярного выражения \b является шаблоном границы слова или (\W\w|\w\W). Регулярное выражение читается как "Найти последовательность, состоящую из целого слова 'the' или 'of', за которым следует небуквенный символ". Поясню, почему так необходим оператор \W+: \b - это пустая строка в начале или конце слова. Необходимо поставить одну или несколько букв между словами, иначе регулярное выражение не найдет совпадения.

Берем то, что нам нужно

Нахождение текста - это очень общая задача, но, как правило, необходимо извлечь необходимый текст после того, как он был найден. Другими словами, нужно достать иголку и отбросить стог сена.

Регулярное выражение извлекает информацию через захват. Если необходимо отделить необходимый текст от остального, то нужно поставить шаблон в скобки. Раньше мы уже использовали скобки для объединения выражений; по умолчанию скобки осуществляют захват автоматически.

Чтобы продемонстрировать захват, перейдем на Perl. (Утилита grep не поддерживает захват, так как ее цель - это печать строк, содержащих шаблон.)

Команда:

perl -n -e '/^The\s+(.*)$/ && print "$1\n"' heroes.txt

выведет:

Tick
Punisher

Команда perl -e позволяет запустить команду на языке Perl прямо из командной строки. Команда perl -n запускает программу для каждой строки входного файла. Часть команды, являющаяся регулярным выражением, текст между двумя слешами (/) говорит: "Найти строку, начинающуюся буквами 'T', 'h', 'e', за которыми следует один или более пробелов, \s+, затем захватить все символы до конца строки".

Захваченный в Perl текст помещается в специальные переменные Perl, начиная с $1. Оставшаяся часть программы на Perl печатает захваченный текст.

Каждая вложенная пара скобок, считая слева с учетом уровня вложенности скобок, захватывает текст в соответствующую числовую переменную Perl ($1, $2 и так далее). Например,

perl -n -e '/^(\w)+-(\w+)$/ && print "$1 $2"'

дает следующий результат:

Spider Man
Ant Man
Spider Woman

Захвата нужного текста обычно недостаточно. Как правило, необходимо заменить его на какой-нибудь другой. Редакторы, такие как vi и Emacs, объединяют шаблоны поиска и замены, чтобы найти и сразу же заменить текст. Также можно изменять текст из командной строки, используя шаблоны замены и редактор sed.

Важные замечания

Регулярные выражения обладают невероятными возможностями; разновидностей операторов, а также методик их применения в командах невероятно много. По этой теме есть так много информации и практического опыта, что невозможно пересказать здесь даже их малую часть.

К счастью, существует три больших источника информации по теории и практике применения регулярных выражений:

  • Если в системе установлен Perl, то следует прочитать страницы man в Perl, посвященные регулярным выражениям (они вызываются командой perldoc perlre). На этих страницах дано прекрасное введение в регулярные выражения и множество полезных примеров. Многие языки программирования используют адаптированные совместимые регулярные выражения (Compatible Regular Expressions - PCRE) языка Perl, поэтому, прочитав эти страницы man, можно применять полученные знания непосредственно в языках PHP, Python, Java™, Ruby, а также во многих современных программах.
  • Книга Regular Expressions (третье издание), автором которой является Джеффри Фридл (Jeffrey Friedl), рассматривает множество вопросов использования регулярных выражений. Тщательно, аккуратно, понятно и практично в этой книге рассказывается о том как работает сопоставление, все операторы регулярных выражений, повторяемость (ограничение количества символов операторами + и *) и многое другое. Кроме того, эта книга содержит несколько элегантных регулярных выражений по правильной обработке корректных адресов электронной почты и других строки Request for Comments (RFC).
  • Натан Гуд (Nathan Good), автор книги Regular Expression Recipes, представляет полезные решения по обработке данных и фильтрации. Необходимо извлечь из данных почтовый код, телефонный номер или строку в кавычках? Попробуйте решения, описанные в этой книге.

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

Например, используя команду ls [a-c] можно найти файлы с именами a, b и c. Команда ls [a-c]* найдет все файлы, имена которых начинаются на a, b и c. Здесь * не модифицирует [a-c] так, как это делает утилита grep; в командной оболочке * интерпретируется как .* из регулярных выражений. Оператор ? работает в командной оболочке, но интерпретируется как . , то есть совпадение с любым одиночным символом.

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

Урок окончен!

Сегодняшний урок длился дольше обычного. Но теперь вы знаете основы регулярных выражений. К следующему занятию, я подготовлю урок на классическую тему "99 строк о 99 командах".

Ресурсы

Научиться

Получить продукты и технологии

  • IBM trial software: ознакомительные версии программного обеспечения для разработчиков, которые можно загрузить прямо со страницы сообщества developerWorks.(EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=294961
ArticleTitle=Использование UNIX: Часть 9. Регулярные выражения
publish-date=03142008