- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF139:regexp
Материал из Linuxformat.
- Hardcore Linux Проверьте себя на крутом проекте для продвинутых пользователей
Содержание |
Программируем: регeкспы
- Они не столь и ужасны. Нет, правда. Грэм Моррисон разоблачает одну из последних хитростей времен командной строки.
Для большинства из нас регулярные выражения – регекспы – загадочны, как египетские иероглифы. Причудливая смесь символов, букв и цифр вдруг образует всемогущее заклинание, превращающее свинец в золото, а имена файлов – в каталоги. Регекспы – признак профессионала, вечно занятого системного администратора, не снисходящего до повторения однотипных действий, или программиста, изыскивающего фрагменты кода или оптимизирующего код на Perl.
Но регулярные выражения также невероятно полезны почти всем, в том числе новичкам – для простой работы с файлами из командной строки, простого и быстрого поиска и замены или пакетной обработки. Вы обнаружите, например, что многие текстовые редакторы поддерживают регулярные выражения в большинстве функций для обработки файлов и поиска. В Google Code Search регулярные выражения добудут вам миллионы строк публично доступного кода,и даже в окне «Поиск и Замена» OpenOffice.org можно пользоваться регулярными выражениями для поиска по документам. Пусть вы не хотите пачкать руки, изучая, как они работают – каждое выражение является самодостаточным рецептом, который легко применять в различных ситуациях; поэтому неплохо завести себе библиотеку полезных выражений.
В общем случае, регулярные выражения – это текстовые шаблоны, способные заменить нередко громоздкую альтернативу при поиске по строкам. Например, можно искать имена файлов, содержащие набор цифр, или заменить все вхождения слова «овсянка» на «мюсли» в папке с текстовыми документами. Они используются в ряде важнейших утилит командной строки, и даже в мощных графических приложениях. Но прежде чем начать их применять, разберемся, где это допускается. Во многих мощных утилитах командной строки, типа grep, sed и awk, их можно использовать «напрямую», хотя эти программы воспринимают символы немного по-разному. В командной строке любой из версий Bash' после третьей регекспы в той или иной степени применимы во встроенных командах, таких как ls или rm, или в скриптах, запускаемых из оболочки.
Работа с файлами
Вам будут уже знакомы некоторые концепции, лежащие в основе регулярных выражений, потому что они напоминают использование спецсимволов в маске файла. Например, поиск всех файлов JPEG с маской *.jpg можно классифицировать как простейший тип регулярного выражения. Этот пример легко расширить, заменив * символом ?, означающим не строку символов, а один символ:
s picture?.* # Поиск в Bash файлов изображений с именами, состоящими из слова ‘picture’ и еще одного символа, и любым расширением.
То же справедливо и для регулярных выражений, в которых символы для фильтров и процессов используются для построения модульного решения. Спецсимволы внутри регулярных выражений называются метасимволами, и хотя в них есть нечто общее с тем, к чему вы привыкли в Bash, имеются и различия. Например, ? и * используются иначе, и существует довольно длинный список метасимволов, используемых для построения модульной функции поиска. Но идея все та же – нудный ручной подход заменяется автоматическим. Изучение этих символов и их использования – ключ к работе с регулярными выражениями, и командная строка – как всегда, лучшее место для старта.
Perl пришел в жизнь как язык для генерации отчетов, вследствие чего поддерживает регулярные выражения так, как никакой другой. Пусть вы не хакер Perl – в командной строке есть много Perl-подобного. Прекрасный образчик – отличная команда rename: это один из наиболее широко используемых скриптов на Perl и потому принимает регулярные выражения без добавочных преобразований. Вот хороший пример:
rename ‘s / htm$ / html / ’ *.htm # Переименовать все файлы с расширением ‘.htm’, обычные для Windows, в более употребительные ‘.html’.
Строка, следующая за командой rename, должна быть заключена в одиночные кавычки; первая s в строке означает замену [substitution] – то, в чем Perl особенно хорош (возможны, конечно, и другие действия). Каждый аргумент замены отделяется прямым слэшем, и замена выполняется в соответствии с двумя приведенными регулярными выражениями. Первое выражение определяет часть имени файла, которая будет изменена, а вторая – файлы, у которых будут изменены имена. Знак доллара соответствует символам в конце входной строки, которая в данном случае является расширением файла, а звездочка соответствует всем входным файлам, аналогично выбору файлов по маске в командной строке.
Впрочем, заменять можно не только видимые символы, но и те, что не видны, например, пробелы.
$ rename ‘s/ *//g’ *.ogg # Удалить все пробелы в файлах с музыкой, взятые из имен дорожек CD.
Это выражение похоже на пример с ‘html’. Производится поиск пробела, за которым следует звездочка, означающая «все, что угодно», и все найденные пробелы заменяются на «ничто» (т. е. удаляются), поэтому мы видим два прямых слэша подряд. Завершающая g в регулярном выражении – обозначение глобальной замены из Perl – гарантирует, что замена будет произведена во всех входных файлах, а не только в первом из обнаруженных.
Вторая популярная (даже слишком) команда – tr. Она преобразует одну строку в другую подобно команде rename, но работает не с файлами, а со стандартным вводом. Она немного напоминает «sed для бедных», и вы ее освоите, если когда-либо заглядывали в справку по sed. Стандартный ввод tr – поток данных, передаваемый команде через канал Bash |, который существенно отличается от канала, используемого регулярными выражениями. В команде tr интересно то, что она вводит в регулярные выражения идею списков. Список определяется в квадратных скобках – и это удобный способ перечислить конкретные значения или указать диапазон связанных. Команда tr преобразует значения входного списка в значения, описанные выходным списком:
cat file.txt | tr ‘[(abc|dog)]’ ‘[efg]’ > file_tr.txt # Найти и заменить строку ‘abc’ внутри текстового файла строкой ‘efg’, и поместить их в новый файл с именем ‘file_tr.txt’
Есть масса способов применения этой команды, но приведенный выше пример – наиболее общий. Он использует стандартный ввод вместо приема файла в качестве аргумента, и мы перенаправляем вывод file.txt командой cat. Следующая часть – наш первый настоящий пример построения регулярного выражения; подобные ему встречается чуть ли не во всех регулярных выражениях. Мы используем два списка, что показывают квадратные скобки, для определения строки ввода и строки, которая заменит каждое вхождение, а затем вывод будет перехвачен и направлен в file_tr.txt.
Усложним задачу
Полюбопытствуем фрагментами регулярного выражения в скобках – замены abc на efg, так как эти списки не обязаны состоять из букв. Например, [a-z] означает все буквы между a и z. Естественно, то же самое справедливо для цифр: просто замените символы в скобках. Хотя лучшее доказательство мощи таких выражений – регулярное выражение, выполняющее поиск шестнадцатеричного символа. Этого легко достичь конструкцией [0‑9a-fA-F], которая допускает буквы от A до F и все цифры.
Другая возможность регулярных выражений, используемых tr – управляющие символы. Эти символы видны только по результату своего действия – например, возврат каретки или табуляция; и внутри регулярного выражения они представляются в виде обратного слэша, за которым следует другой символ. Например, табуляция – это \v, символ перехода на новую строку – \n, и есть масса других. С помощью этих символов в выражениях для поиска и замены можно отформатировать ваш текст.
Но когда дело доходит до поиска текста в файле, существует только одно действительно хорошее средство, и это grep. Принимая единственный аргумент поиска и имя файла с текстом, эта утилита выведет все строки файла, содержащие искомую строку. Более того, регулярные выражения прекрасно можно использовать и в строке поиска, и в имени файла, и grep решит задачу точно таким же способом, что делает утилиту отличным средством формирования отчетов, если входных файлов достаточно. Можно напустить grep на файлы системных журналов – например, для поиска подозрительной активности или для создания отчета о том, кто и сколько пользовался системой. И это прекрасный испытательный стенд для ваших вновь обретенных знаний о регулярных выражениях, так как она не затронет исходные файлы, а вы сразу увидите, увенчался ли поиск успехом.
grep ‘\(dog\|cat\)s’ pets.txt # Поиск собак ‘dogs’ или кошек ‘cats’ в текстовом файле.
Другая удобная возможность для поиска текста – оператор условия |. С его помощью можно задать несколько альтернатив для соответствия в регулярном выражении. Им можно воспользоваться, например, когда нужно учесть разнотык в написании имен или когда есть список альтернатив. Добавить опцию or нетрудно: нужно просто разделить ею все строки и дописать ко всем строкам, кроме последней, символ \. Он нужен для распознавания символа |, а также необходим в начале списка в скобках: именно по нему парсер узнает, что в скобках находится список опций. Все, что идет сразу после скобок, перемещается в конец строки поиска, и мы пользовались этой возможностью в примере выше, чтобы добавить букву s к концу dog или cat, для учета форм множественного числа.
grep 192\.168\.1\.* addresses.txt # Поиск в текстовом файле указанного диапазона IP-адресов.
Как мы убедились, обратный слэш можно использовать весьма причудливым образом. Например, нужен поиск по IP-адресу; но точки в обычной записи IP-адреса в регулярном выражении будут означать что-то другое. Поэтому, как и в предыдущем примере, каждую точку нужно предварить обратным слэшем. В последних версиях grep это делать необязательно, но лучше все-таки сделать, чтобы исключить любые неопределенности, если вы хотите с кем-нибудь поделиться этим выражением. Со слэшами в регулярном выражении одно и то же выражение можно будет использовать с разными утилитами.
grep ‘^.*example[^.]*line.*\.’ test.txt 1 # Поиск слов ‘example’ и ‘line’ внутри одного предложения.
Понимание регекспов
Приведенный выше пример – хорошая иллюстрация того, как, приложив совсем немного усилий, мы почти напрочь сорвали покров таинственности с регулярных выражений – мы уже рассмотрели все их компоненты. Карат в начале строки поиска ищет начало новой строки или абзаца, отделенного возвратом каретки. Следующие за ним .* соответствуют любому количеству символов после этой точки, пока не будет найден example. Далее идет хитрый кусочек – [^.] означает, что поиск будет считаться успешным, только если между словами example и line нет точки, то есть оба слова находятся в одном предложении. Наконец, символы .*\. в конце строки поиска ищут весь остальной текст, прежде чем снова искать точку в конце того же предложения. В конечном результате мы получаем гибкий поисковый алгоритм, который можно модифицировать для различных типов поисковых задач, а так как он использует grep, можно воспользоваться некоторыми его функциями, чтобы упростить выражение. Например, можно добавить ключ -i, чтобы выражение было нечувствительным к регистру, или ключ -c, чтобы подсчитать количество соответствий.
sed ‘s / dog / / g’ pets.txt # ‘sed’ сканирует текстовый файл и удаляет все вхождения слова‘dog’.
После grep, самая популярная утилита, использующая регулярные выражения – пожалуй, sed. Ко всему хорошему, она добавляет обработку текста, позволяя менять содержимое файла с помощью регулярных выражений. В простейшем случае вы задаете искомый текст и имя функции для обработки этого текста. Примените ее к файлу, и вывод на экран должен отразить эти изменения. Но благодаря регулярным выражением, она может стать куда сложнее.
В приведенном выше примере первая s активизирует режим замены sed. Это меняет поведение «до следующего /» на «до второго /». К тому же в этом примере нет строки замены. Два прямых слэша вместо нее означают, что первое слово будет заменено «ничем» между слэшами, то есть, по сути, удалено. Чтобы изменения не затронули исходный файл, sed направляет свой вывод в терминал – вы быстро увидите, все ли работает. Если вы хотите сохранить вывод в другом файле, просто добавьте >newfile.txt.
sed ‘s / <[^>]*> / / g’ index.html # Убрать все HTML-тэги на web-странице, оставив только текст
Режим замены может быть использован для решения множества задач, включая многие из решаемых grep. Например, чтобы удалить HTML-тэги из файла web-страницы, можно соединить символы для поиска начала тэга, выбрать текст между символами открытия и закрытия тэга и удалить их точно так же, как в предыдущем примере. Сложный фрагмент кода в середине выделяет текст внутри скобок и использует его в качестве замены.
Теперь мы знакомы с основами регулярных выражений и c примерами их применения. С помощью одного из этих методов вы сможете расширить некоторые из наших идей и превратить простые запросы в более сложные. Если вы побаиваетесь командной строки, необходимый функционал можно найти в приложениях вроде OpenOffice.org или Kodos.
Актеры и роли
Вот краткое описание значений каждого из этих специальных символов.
Метасимволы лежат в основе регулярных выражений, но именно из-за них несложное выражение больше напоминает формулу для вычислений, чем алгоритм поиска и замены. Дело в том, что для обозначения специальных функций используются самые редкие символы, чтобы их нельзя было спутать с настоящими строками поиска. В результате из-за них регулярное выражение выглядит сложнее, чем оно есть. Но как и с любым новым языком, знание пары-тройки символов существенно помогает пониманию, и вот список наиболее распространенных.
- . (точка) Означает один символ в масках файлов, как ?, но также включает невидимые символы вроде пробела. Но не включает символ перевода строки – это важно помнить, выполняя поиск по коду.
- * (звездочка, звездочка Клини) Аналогично значению в интерактивной оболочке, звездочка заменяет ноль или более символов, предшествующих выражению: так, a*z соответствует всем словам, начинающимся с ‘a’ и заканчивающимися на ‘z’, но также и тем, что просто заканчиваются на ‘z’.
- ^ (карат, клин, шляпа, домик) Это один из самых распространенных символов в регулярных выражениях, и он означает просто начало входной строки – например, первую букву имени файла.
- $ (песо, доллар) Имеет противоположное карату значение и соответствует символам в конце входной строки, например, расширению файла.
- () (содержимое внутри скобок) Содержат одно выражение, так что оно может использоваться вместе с другими фрагментами кода.
- | (вертикальная черта, канал) Как и в некоторых языках программирования, вертикальная черта – логическое ИЛИ, используемое в тех случаях, когда нужно произвести поиск по нескольким выражениям, заключенным в скобки.
- [] (содержимое внутри квадратных скобок) Квадратные скобки обычно содержат список символов, для которых нужно задать одно вхождение. Это особенно удобно, когда нужно искать по набору символов, у которых нет ничего общего.
- [^] Добавление символа карата в начале списка в квадратных скобках меняет его действие на противоположное: теперь его значение соответствует символу, не содержащемуся в скобках.
Построение регулярных выражений через GUI
Регулярные выражения бывают настолько сложны, что крышу сносит. И даже если вы умеете их писать, создание выражения посложнее, работающего должным образом – обычно путь проб и ошибок. В обоих случаях поможет визуальная среда построения регулярных выражений. Она может выступать как интегрированная среда разработчика для построения выражений, отображать результаты в реальном времени с добавлением и удалением компонентов и подсвечивать все ошибки в выражении по мере их появления.
В KDE 3 была прекрасная утилитка, которая использовалась как виджет в приложениях вроде KMail и как самостоятельное приложение. К сожалению, KRegExEditor не пережил перехода на KDE 4. Лучшая альтернатива, которую мы смогли найти – Kodos. Это утилита для регулярных выражений в Python, но она прекрасно подойдет для построения любых других регулярных выражений. Вы вводите выражение в верхнее поле и фрагмент искомой строки в нижнее, и на вкладках в нижнем окне появится информация об интерпретации и того, и другого. Например, можно ввести выражение для проверки адреса электронной почты в верхнее поле и сам адрес в поле поиска, и если выражение работает правильно, вы увидите адрес в поле соответствия.