Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Регулярные выражения

Резюме: Регулярные выражения используются для расширенного контекстного поиска и модификации текста. Они могут быть во многих профессиональных редакторах, в программах синтаксического анализа (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 во второй позиции и любым первым символом.

Если определение класса начинается с "[^" вместо "[" то это означает отрицание этого класса (все символы кроме указанных). Теперь "^" больше не означает начало строки, но в комбинации с "[" обозначает отрицание класса.



[0-9]    Односимвольный шаблон, который соответствует


         любой цифре от 0 до 9. 


[^0-9]   Соответствует любому одному НЕ цифровому символу.


[^abc]   Соответствует любому символу, который не a,b или c.


 .       Точка соответствует любому символу исключая символ новой строки.


         Это тоже самое как [^\n]. Где \n - символ новой строки.





Для нахождения всех строк, которые не начинаются с 1 мы можем


написать:


grep '^[^1]' phonelist.txt


или


egrep '^[^1]' phonelist.txt


Фиксирование шаблонов

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



^  Соответствует началу строки


$  Соответствует концу строки


Чтобы найти человека с ID номер 567 в нашем phonelist.txt мы можем использовать:



egrep '567$' phonelist.txt


Это покажет строки с числом 567 в конце.

Множители

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

Описание grep egrep perl vi vim vile elvis emacs
ноль или больше раз * * * * * * * *
один или больше раз \{1,\} + + \+ \+ \+ +
ноль или один раз \? ? ? \= \? \= ?
от n до m раз \{n,m\} {n,m} \{n,m\} \{n,m\}

Примечание: Различные VI'и имеют магические опции установки для того чтобы они работали как показано выше.

Пример из телефонного списка:



....


1248   Kate 634


....


1548  Kerry 534


....


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



grep '^1[0-9]\{1,\} \{1,\}K' phonelist.txt


или использовать * и повторить [0-9] и пробел:


grep '^1[0-9][0-9]*  *K' phonelist.txt


или


egrep '^1[0-9]+ +K' phonelist.txt


или


perl -ne 'print if (/^1[0-9]+ +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 ${1}
vi,vim,vile,elvis \(\) \1
emacs \(\) \1

Пример:



Выражение [a-z][a-z] будет


соответствовать двум буквам 


в нижнем регистре.


теперь мы можем использовать эти переменные для создания шаблонов для поиска текста такого как 'otto':



egrep '([a-z])([a-z])\2\1'





Переменная \1 содержит букву о


и переменная \2 букву t. 





Это будет соответствовать имени anna, но


не yxyx.


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

Использование регулярных выражений для редактирования текста

Для редактирования вам необходим редактор такой как 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'е вы можете использовать

perl -pe 's/ / /g' 

Посмотрите несколько примеров. План нумерации в нашей компании изменился и во всех телефонных номерах, которые начинаются с 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.)/${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-ю переменную которая кстати пустая. Для преодоления этого мы просто используем ${1}.

Теперь выравнивание в списке немного нарушено. Как вы можете это исправить ? Вы должны просто проверить есть ли пробел в 5-й позиции и если есть, то вставить еще один:



vi:     s/^\(....\) /\1  /g


emacs:  '^\(....\) '  replaced by  '\1  '


perl:   perl -pe 's/^(....) /${1}  /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-страницы и документацию вашего любимого редактора и изучить подробности.

Есть еще много других специальных символов таких как, например символы условий, типа "или" и также символы границ слов, упоминаемые выше.

Удачи, счастливого редактирования.

Перевод: Михаил Литвак Сергей Колычев