Регулярные выражения
Резюме : Регулярные выражения используются для расширенного контекстного поиска и модификации текста. Они могут быть во многих профессиональных редакторах, в программах синтаксического анализа (parser programs) и в языках.
Введение
Регулярные выражения применяются во многих редакторах таких как vi и emacs, в программах grep/egrep и языках таких как awk, perl и sed.
Регулярные выражения используются для расширенного контекстного поиска и модификации текста. Регулярное выражение — это формальное описание шаблона, который соответстует текстовой строке.
Когда я увидел несколько лет назад человека, использующего регулярные выражения, я был очарован. Задачи редактирования и поиска текста, которые обычно занимают часы, могут быть выполнены просто за несколько секунд. Однако я не понимал тех выражений, которые я увидел на экране. Они выглядели как странная комбинация точек, слешей, звездочек и некоторых других символов. Тогда я решил узнать как они работают и в последствии я обнаружил, что они очень легки в использовании. Они подчиняются нескольким простым синтаксическим правилам.
Хотя регулярные выражения очень широко распространены в мире Unix, но не существует такой штуки как ‘стандартный язык регулярных выражений’. Существует несколько различных диалектов. Например существует два типа программы grep; grep и egrep. Обе используют регулярные выражения с незначительно отличающимися возможностями. Perl возможно имеет более полный набор регулярных выражений. К счастью все они следуют общим принципам. Как только вы поймете основную идею, то вам будет легко изучить детали различных диалектов.
Эта статья введет вас в основы и вы сможете изучить различные аспекты и возможности программ на их man-страницах.
Простой пример
Пусть вы имеете список телефонов компании и он выглядит подобно этому:
Phone Name ID . . 3412 Bob 123 3834 Jonny 333 1248 Kate 634 1423 Tony 567 2567 Peter 435 3567 Alice 535 1548 Kerry 534 .
Это компания из 500 человек. Они хранят данные в простом текстовом файле. Человек с первой цифрой телефонного номера равной 1 работает в строении N 1. Кто работает в строении 1 ?
Регулярные выражения дающие ответ: grep '^1' phonelist.txt или egrep '^1' phonelist.txt или perl -ne 'print if (/^1/)' phonelist.txt
Синтаксис
Односимвольные шаблоны
Основным строительным блоком регулярных выражений является односимвольный шаблон. Он соответствует просто этому символу. Примером односимвольного шаблона является 1 в предыдущем примере. Она просто соответствует единице в тексте.
Другой пример односимвольного шаблона: egrep 'Kerry' phonelist.txt
Этот шаблон состоит только из односимвольных шаблонов (буквы K,e. )
Символы могут быть сгруппированы вместе в класс. Класс представляется как пара из открывающей и закрывающей квадратных скобок и списка символов между ними. Класс представляет собой также односимвольный шаблон. Один и только один из этих символов должен присутствовать в тексте, который совпадет с шаблоном. На пример:
[abc] Односимвольный шаблон, который соответствует какой либо из букв a, b или c. [ab0-9] Односимвольный шаблон, который соответствует либо a, либо b, либо цифре в интервале от нуля до девяти. [a-zA-Z0-9\-] Это соответствует либо букве в верхнем или нижнем регистре, цифре или знаку минус. Попробуйте это: egrep '^1[348]' phonelist.txt
Это ищет строки, которые начинаются с 13 или 14 или 18.
Мы уже увидели, что большинство ASCII символов просто соответствуют этим же ASCII символам, но некоторые ASCII символы, называемые метасимволами, имеют специальное значение. Например квадратные скобки ограничивают класс. Смысл «-» в классе это интервал. Чтобы отменить специальное значение метасимвола нужно поставить перед ним обратный слеш. Пример этого — знак минус в [a-zA-Z0-9\-]. В некоторых диалектах регулярных выражений метасимволы начинаются с обратного слеша. В этом случае вы должны удалить обратный слеш, что бы получить нормальное значение символа.
Точка это важный метасимвол. Она соответствует любому символу кроме символа новой строки. Пример:
grep '^.2' phonelist.txt or egrep '^.2' phonelist.txt
Эти выражения используются для поиска строк с цифрой 2 во второй позиции и любым первым символом.
Если определение класса начинается с «[^» вместо «[» то это означает отрицание этого класса (все символы кроме указанных). Теперь «^» больше не означает начало строки, но в комбинации с «[» обозначает отрицание класса.
9 Односимвольный шаблон, который соответствует любой цифре от 0 до 9. [^0-9] Соответствует любому одному НЕ цифровому символу. [^abc] Соответствует любому символу, который не a,b или c. . Точка соответствует любому символу исключая символ новой строки. Это тоже самое как [^\n]. Где \n - символ новой строки. Для нахождения всех строк, которые не начинаются с 1 мы можем написать: grep '^[^1]' phonelist.txt или egrep '^[^1]' phonelist.txt
Фиксирование шаблонов
В предыдущей части мы уже увидели, что «^» соответствует началу строки. Фиксирование шаблонов производится при помощи специальных символов, которые соответствуют определенной позиции в тексте, а не просто символу.
^ Соответствует началу строки $ Соответствует концу строки
Чтобы найти человека с ID номер 567 в нашем phonelist.txt мы можем использовать:
Это покажет строки с числом 567 в конце.
Множители
Множители определяют сколько раз односимвольный шаблон повторяется в тексте.
Описание | grep | egrep | perl | vi | vim | vile | elvis | emacs |
---|---|---|---|---|---|---|---|---|
ноль или больше раз | * | * | * | * | * | * | * | * |
один или больше раз | \ | + | + | ═ | \+ | \+ | \+ | + |
ноль или один раз | \? | ? | ? | ═ | \= | \? | \= | ? |
от n до m раз | \ | ═ | ═ | ═ | ═ | \ | \ |
Примечание: Различные VI’и имеют магические опции установки для того чтобы они работали как показано выше.
Пример из телефонного списка:
. 1248 Kate 634 . 1548 Kerry 534 .
Для нахождения строк, которые начинаются с 1, и имеют несколько цифр, как минимум один пробел и имя начинающееся с буквы К мы можем написать:
grep '^14\ \K' phonelist.txt или использовать * и повторить 2 и пробел: grep '^134* *K' phonelist.txt или egrep '^15+ +K' phonelist.txt или perl -ne 'print if (/^13+ +K/)' phonelist.txt
Множитель размножает предыдущий односимвольный шаблон. Так «23*4» не означает «2 , затем 3, затем что угодно и 4» (Это выглядит как «23.*4»). А это означает «один раз 2 потом может быть несколько 3 и одна 4»
Так же важно учесть, что множители обладают жадностью. Это означает, что действие первого множителя в шаблоне распространяется направо, так далеко как это возможно.
выражение ^1.*4 будет соответствовать всей строке 1548 Kerry 534 с начала и до самой последней 4. Т.е. оно соответствует не только 154, а всей строке.
Это не имеет большого значения для grep, но важно для редактирования и подстановки.
Круглые скобки как способ запоминания
Круглые скобки не изменяют действие выражения, при этом просто запоминается ограниченная скобками часть текста для того, чтобы можно было ссылаться на нее в выражении позднее.
Запомненная часть доступна через переменные. часть строки ограниченная первой парой скобок доступна через переменную один, вторая через переменную два и т.д.
Имя программы | Синтаксис скобок | Синтаксис переменной |
---|---|---|
grep | \(\) | \1 |
egrep | () | \1 |
perl | () | \1 or $ |
vi,vim,vile,elvis | \(\) | \1 |
emacs | \(\) | \1 |
Выражение [a-z][a-z] будет соответствовать двум буквам в нижнем регистре.
теперь мы можем использовать эти переменные для создания шаблонов для поиска текста такого как ‘otto’:
egrep '([a-z])([a-z])\2\1' Переменная \1 содержит букву о и переменная \2 букву t. Это будет соответствовать имени anna, но не yxyx.
Использование регулярных выражений для редактирования текста
Для редактирования вам необходим редактор такой как vi, emacs или вы можете использовать например perl.
В emacs вы используете M-x query-replace-regexp или вы можете для этого переопределить любую функциональную клавишу. Или вы можете также использовать команду replace-regexp. Команда query-replace-regexp интерактивная, другие нет.
В vi используется команда подстановки :%s/ / /gc. Процент определяет диапазон ‘весь файл’ и может быть заменен любым подходящим диапазоном. В vim вы нажимаете shift-v, помечаете область и тогда используете подстановку только в этой области. Я не буду говорить больше о vim потому, что все должно быть в его собственном руководстве. Интерактивная версия команды это ‘s/ / /gc’, а не интерактивная это s/ / /g
Интерактивность означает, что вы должны будете подтвердить выполнять или нет подстановку в каждом случае.
В Perl’е вы можете использовать
Посмотрите несколько примеров. План нумерации в нашей компании изменился и во всех телефонных номерах, которые начинаются с 1 необходимо вставить 2 после второй цифры.
Например 1423 должно стать 14223.
Старый список: Phone Name ID . 3412 Bob 123 3834 Jonny 333 1248 Kate 634 1423 Tony 567 2567 Peter 435 3567 Alice 535 1548 Kerry 534 .
vi: s/^\(1.\)/\12/g emacs: ^\(1.\) заменяется на \12 perl: perl -pe 's/^(1.)/$2/g' phonelist.txt Теперь новый список телефонов выглядит подобно этому: Phone Name ID . 3412 Bob 123 3834 Jonny 333 12248 Kate 634 14223 Tony 567 2567 Peter 435 3567 Alice 535 15248 Kerry 534 .
Перл может обрабатывать переменные с номерами больше чем от \1 до \9 поэтому \12 будет ссылаться на 12-ю переменную которая кстати пустая. Для преодоления этого мы просто используем $.
Теперь выравнивание в списке немного нарушено. Как вы можете это исправить ? Вы должны просто проверить есть ли пробел в 5-й позиции и если есть, то вставить еще один:
vi: s/^\(. \) /\1 /g emacs: '^\(. \) ' replaced by '\1 ' perl: perl -pe 's/^(. ) /$ /g' phonelist.txt Теперь телефонный список выглядит так: Phone Name ID . 3412 Bob 123 3834 Jonny 333 12248 Kate 634 14223 Tony 567 2567 Peter 435 3567 Alice 535 15248 Kerry 534 .
Коллеги в ручную поредактировали список и случайно вставили несколько пробелов в начале некоторых строк. Как мы можем удалить их ?
Phone Name ID . 3412 Bob 123 3834 Jonny 333 12248 Kate 634 14223 Tony 567 2567 Peter 435 3567 Alice 535 15248 Kerry 534 . Это должно помочь: vi: s/^ *// (Здесь 2 пробела, т.к. мы не имеем +) emacs: '^ +' заменяем пустой строкой perl: perl -pe 's/^ +//' phonelist.txt
Вы пишите программу и в ней у вас есть переменные temp и temporary. Теперь вы хотите заменить переменную temp переменной counter. Если вы просто замените строку temp на counter, то тогда переменная temporary станет counterrary, что видимо не то, что вы хотите.
Регулярные выражения могут сделать это. Просто меняем temp([^o]) на counter\1. Это значит temp без буквы o. (Альтернативным решением может быть использование границ, но мы не рассматриваем этот случай привязки шаблона.)
Я надеюсь, что эта статья вас заинтересовала. Теперь вы можете захотеть взглянуть в man-страницы и документацию вашего любимого редактора и изучить подробности.
Есть еще много других специальных символов таких как, например символы условий, типа «или» и также символы границ слов, упоминаемые выше.
Удачи, счастливого редактирования.