Замечания, дополнения и исправления приветствуются (здесь)!
Урок 1. Введение.
Уроки ориентированы на новичков, с подробными примерами и комментариями. Почти во всех языках программирования используются регулярные выражения, у всех языков есть свои нюансы и синтаксис, поэтому все примеры будут писаться только для AutoIt.
Регулярное выражение (RegExp) - это способ сокращенного представления текста с использованием специальных символов. Думаю все с ними сталкивались при поиске файлов на компьютере: в строке поиска можно набрать *.exe это и есть один из примеров регулярных выражений, под него подходят все имена файлов с расширением exe.
Для чего используют RegExp:
проверка текста на соответствие шаблону
для извлечения информации из текста
для изменения информации в тексте
В AutoIt есть только две стандартные функции для работы с RegExp: StringRegExp и StringRegExpReplace Далее рассмотрим основное назначение и синтаксис этих функций (синтаксис шаблонов рассмотрен поверхностно, более подробно он будет изучен далее)
StringRegExp Используется для проверки на соответствие текста шаблону и для извлечения информации из текста. Синтаксис.
Код
StringRegExp('test', 'pattern'[, flag ][, offset]])
test - непосредственно текст, над которым производятся действия, может быть одной строкой, пустой строкой, элементом массива, многострочным текстом и т.п. (но не пытайтесь передавать в качестве текста массив - результата не будет)
pattert - шаблон текста, по которому будет работать RegExp
flag (не обязательный) - флаг выполнения RegExp, может иметь значение от 0 до 4, по умолчанию flag=0:
flag = 0 Возвращает 0 или 1 (False/True) в зависимости от совпадения текста с шаблоном. Пример проверяет совпадает ли слово "слон" с шаблоном "с..н" (буква "с", два любых символа, буква "н")
Код
$sText = 'слон' $sPattern = 'с..н' If StringRegExp($sText, $sPattern, 0) Then ConsoleWrite('Совпадение!' & @CRLF)
flag = 1 Возвращает массив совпавших элементов. То есть в массив попадают элементы которые попадают под шаблон. Пример выдает совпавшие элементы: "удав" (попадает под шаблон "(.*?)") и "11" (попадает под шаблон "(\d+)").
Код
$sText = 'удав 11 человек 2 крокодил 4' $sPattern = '\s?(.*?)\s(\d+)' $aResult = StringRegExp($sText, $sPattern, 1) For $i = 0 To UBound($aResult) - 1 ConsoleWrite($aResult[$i] & @CRLF) Next
Расшифровка шаблона: "\s?" - знак пробела в количестве 0 или 1 штук, "(.*?)" - скобки означают группа с захватом, ".*" любой символ в количестве, но символ "?" не дает звездочке "съесть" всю строку, а только до появления следующего символа (в нашем случае дальше идет"\s") "\s" пробел одна штука "(\d+)" - снова группа с захватом, "\d+" - любая цифра в любом количестве, но минимум 1 штука
flag = 2 Возвращает массив совпавших элементов и полное совпадение с шаблоном. То есть в массив попадают те же элементы, которые были при flag=1, плюс то что попало под весь шаблон целиком. Пример выдает совпавшие элементы: "удав 11" (попадает под весь шаблон), "удав" (попадает под шаблон "(.*?)") и "11" (попадает под шаблон "(\d+)").
Код
$sText = 'удав 11 человек 2 крокодил 4' $sPattern = '\s?(.*?)\s(\d+)' $aResult = StringRegExp($sText, $sPattern, 2) For $i = 0 To UBound($aResult) - 1 ConsoleWrite($aResult[$i] & @CRLF) Next
flag = 3 Возвращает массив глобально совпавших элементов. То есть в массив попадают элементы которые попадают под шаблон, но после нахождения первого совпадения с шаблоном поиск продолжается. Пример выдает совпавшие элементы: "удав", "человек", "крокодил" (попадают под шаблон "(.*?)") и "11", "2", "4" (попадают под шаблон "(\d+)").
Код
$sText = 'удав 11 человек 2 крокодил 4' $sPattern = '\s?(.*?)\s(\d+)' $aResult = StringRegExp($sText, $sPattern, 3) For $i = 0 To UBound($aResult) - 1 ConsoleWrite($aResult[$i] & @CRLF) Next
flag = 4 Возвращает массив массивов полного совпадения с шаблоном и совпавших элементов. То есть на выходе мы получаем массив элементами которого будут являться другие массивы. Пример выдает массивы совпавших элементов и полное совпадение с шаблоном: 1 массив - "удав 11", "удав", "11" 2 массив - " человек 2", "человек", "2" 3 массив - " крокодил 4", "крокодил", "4".
Код
$sText = 'удав 11 человек 2 крокодил 4' $sPattern = '\s?(.*?)\s(\d+)' $aResult = StringRegExp($sText, $sPattern, 4) For $i = 0 To UBound($aResult) - 1 $aTemp = $aResult[$i] For $z = 0 To UBound($aTemp) - 1 ConsoleWrite($aTemp[$z] & @CRLF) Next Next
Самыми распространенными являются флаги 0 и 3, поскольку при флаге 0 результат менее информативен, в примерах я буду использовать flag=3
offset (не обязательный) - указывает позицию в строке, с которой следует начинать поиск, первый символ в строке равен 1, по умолчанию offset = 1 Пример проверяет соответствие текста шаблону "с", при условии начала поиска со второй буквы, но поскольку в тексте буква "с" встречается только на первой позиции, то выдачи не будет
Код
$sText = 'слон' $sPattern = 'с' If StringRegExp($sText, $sPattern, 0, 2) Then ConsoleWrite('Совпадение!' & @CRLF)
StringRegExpReplace Главным образом используется для изменения информации в тексте, но также может извлекать информацию *с ограничениями (будет рассмотрено ниже) Синтаксис.
Расшифровка шаблона: "\d+" - любое количество цифровых символов, но не менее 1 штуки
Кроме обычных замен текстом replace поддерживает обратные ссылки (back-reference), то есть может манипулировать найденными по шаблону элементами. Существует ограничение на количество ссылок - максимум 10, обозначение ссылок "$0-$9" или "\0 - \9", обратная ссылка "\0" или "$0" включает в себя полное совпадение с шаблоном Пример показывает, как используя обратные ссылки производится перестановка слов и чисел местами:
Расшифровка шаблона: "(\S+)" - группа с захватом (которая будет являться обратной ссылкой "$1") в которую входит любое количество "не пробелов" в количестве минимум 1 штуки "\s" - пробел "(\d+)" - группа с захватом (которая будет являться обратной ссылкой "$2") в которую входит любое количество цифр в количестве минимум 1 штуки "\s?" - пробел в количестве 0 или 1 штуки На выходе получаем "$2 $1 ", то есть "число" "пробел" "не пробелы" "пробел" и т.д. пока не кончится текст. Или можно выводить найденные элементы без всяких перестановок. Пример выводит число, идущее после слова "человек"
Расшифровка шаблона: ".*?к\s" - любое количество любых символов пока не встретим русскую букву "к" с пробелом после нее "\s" "(\d+)" - группа с захватом (которая будет являться обратной ссылкой "\1") в которую входит любое количество цифр в количестве минимум 1 штука ".*" - любое количество любых символов (это сочетание "съест" всю строчку до конца)
Важное замечание: при работе с обратными ссылками сразу после ссылки нельзя ставить цифры!, т.к. RegExp будет принимать их за ссылки и некорректно обрабатывать. Но есть решения для данной ситуации: 1. Нужно заключить цифру ссылки в фигурные скобки.
Способы 2 и 3 оставлены для истории - они были составлены в те далекие времена, когда еще не было известно о способе 1
ЦитатаHistory
2. Добавить сразу после ссылки какой-либо символ (например "#" или "&"), а после удалить его любым способом, хоть другим RegExp-ом. В примере нужно заменить пробелы между словом и числом на цифру "1":
$sText = 'удав 11 человек 2 крокодил 4' $sPattern = '(\S+)\s(\d+)\s?' $sResult = StringRegExpReplace($sText, $sPattern, '$1&&&1$2 ') ; добавим после $1 три амперсенда $sResult = StringRegExpReplace($sResult, '&&&', '') ; заменяем все сочетания из трех амперсендов на пусто("") ConsoleWrite($sResult & @CRLF) ; выдаст нужный результат
3. Дописать в конец текста необходимую цифру и использовать ее через обратные ссылки. В примере нужно заменить пробелы между словом "человек" и следующим числом на цифру "1":
$sText = 'удав 11 человек 2 крокодил 4' $sPattern = '(.*?к)\s(.*)(\d)' ; меняем шаблон $sResult = StringRegExpReplace($sText & '1', $sPattern, '03$2 ') ; дописываем к тексту "1" ConsoleWrite($sResult & @CRLF) ; выдаст нужный результат
Расшифровка шаблона: "(.*?к)" - группа с захватом (которая станет обратной ссылкой "$1"): любое количество любых символов пока не встретим русскую букву "к" "\s" - пробел "(.*)" - группа с захватом (которая будет являться обратной ссылкой "$2") в которую входит любое количество любых символов ("съедает" всю строку, но после возвращает один символ в следующую группу) "(\d)" - группа с захватом (которая будет являться обратной ссылкой "$3") цифра как последний символ в строке (в нашем случае это "1") Последнее решение работает только для полного шаблона. (Для данного примера не получится заменить все пробелы)
count (не обязательный) - количество замен, которые нужно сделать, по умолчанию count = 0, т.е. совершить глобальные (все возможные) замены в тексте
Урок 2. Шаблоны
Элементы шаблона Шаблон для RegExp состоит из обычных и специальных символов. К обычным символам (литералам)относятся: 1. буквенные символы - русский и английский алфавиты 2. цифровые символы - 0 1 2 3 4 5 6 7 8 9 3. некоторые нелитеральные символы
Код
# % = , < > ! ` ~ @ & - _ / ; : " '
Фигурные/полукруглые скобки "{" "}" , могут использоваться как спец символ для обозначения повтора (будет рассмотрено ниже) "н{15}" - совпадает с повторением символа "н" ровно 15 раз "н{15t}" - совпадает со строчкой вида "н{15t}" К специальным символам (метасимволам)относятся:
Код
. ^ $ \ ( ) [ ] * + ? { } |
Любой символ обозначает себя самого, если это не метасимвол.
Метасимволы
"." - соответствует любому одиночному символу, кроме символа новой строки (как убрать ограничение будет рассмотрено позже) Пример выводит все трехбуквенные слова, начинающиеся с буквы "j":
Код
#include <Array.au3> $sText = 'jan feb mar apr may jun jul aug sep oct nov dec' $sPattern = '(j..)' $aResult = StringRegExp($sText, $sPattern, 3) _ArrayDisplay($aResult)
"^" - совпадает с началом строки (для мультистрокового текста совпадает с началом каждой строки, но для этого необходимо включить спец флаг "(?m)", его рассмотрим позже) Пример выводит строки из мультистрокового текста, начинающиеся с буквы "j":
Код
#include <Array.au3> $sText = 'jan feb mar' & @LF & 'apr may jun' & @LF & 'jul aug sep' & @LF & 'oct nov dec' $sPattern = '(?m)^(j.*)' $aResult = StringRegExp($sText, $sPattern, 3) _ArrayDisplay($aResult)
Другой пример выводит весь текст, после 4-го символа строки:
"$" - совпадает с концом строки (для мультистрокового текста совпадает с концом каждой строки, но для этого необходимо включить спец флаг "(?m)", его рассмотрим позже) Пример выводит весь текст, за исключением последних 4-го символов строки:
"\" - символ экранирования, используется для отмены действия метасимвола, в случаях когда вам нужно использовать метасимвол как обычный литеральный. Пример показывает обработку строки с использованием экранирования ")" и ".":
"( )" - групповой шаблон с захватом в память, в котором все элементы идут в указанном поряде. К группе могут применяться символы повтора. Пример выводит имя и все цифровые параметры после него:
Расшифровка шаблона: "\s?" - пробел в количестве 0 или 1 штуки "(.*?" - начало группы с захватом: любой символ в любом количестве, но знак "?" не дает "съедать" строку дальше "(?:\s\d+)+" - группа без захвата (будет рассмотрено дальше): пробел, после которого идет минимум один цифровой символ, вся группа повторяется минимум 1 раз ")" - собственно конец группы с захватом
"(?: )" - групповой шаблон без захвата. Ничем не отличается от предыдущего, но не сохраняется для дальнейшего вывода.
"[ ]" - любой символ из набора. Для внесения в набор непрерывных сочетаний, например, от "щ" до "я" используется символ дефиса "[щ-я]", если необходимо внести сам дефис в набор, его пишут в начале/конце набора или экранируют, кроме того нужно экранировать "]", "[", "\" и "^". Все метасимволы, попавшие в набор теряют свои свойства и становятся обычными символами. Пример выводит результат разбиения строки по символу "|"
Расшифровка шаблона: "([а-яА-Я\s]+)" - группа с захватом, внутри которой повтор символа набора минимум 1 раз, к символам набора относятся: строчные и прописные русские буквы от а до я и символ пробела. Поскольку в наборе нет символа "|", то на нем данная группа прервется и начнет поиск дальше.
Цитатабуква "ё"
В набор "а-я" и "А-Я" не попадают буквы "ё" и "Ё" соответственно. Их нужно дописывать в набор отдельно, либо использовать такие диапазоны: "а-ё" для строчных букв и "Ё-Я" для прописных. Из проведенного ниже примера видно, что буква "ё" для RegExp в русском алфавите идет после буквы "я", буква "Ё" идет перед буквой "А". Следовательно полный набор строчных русских букв попадает под шаблон "[а-ё]", полный набор прописных русских букв попадает под шаблон "[Ё-Я]", а совмещение этих шаблонов выглядит так: "[а-ёЁ-Я]" или "[Ё-Яа-ё]". Пример:
Код
#include <Array.au3> $sText = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ' Dim $aPattern[8] = ['[а-я]', '[а-ё]', '[А-Я]', '[Ё-Я]', '[а-яА-Я]', '[а-ёЁ-Я]', '[я-ё]', '[Ё-А]'] For $i = 0 To UBound($aPattern) - 1 $aResult = StringRegExp($sText, $aPattern[$i], 3) ConsoleWrite($aPattern[$i] & @CRLF) ConsoleWrite(_ArrayToString($aResult) & @CRLF) ConsoleWrite('--------------------------------------------------' & @CRLF) Next
"[^ ]" - ни один символ из набора. Схема работы как и у "любой символ из набора", но проверяется чтобы символ не был в данном наборе. Для включения в набор символа "^" его нужно экранировать или занести первым символом набора "[^abc\^]" или "[^^abc]". провел эксперименты с экранированием "^": его надо экранировать, если он единственный символ набора "[\^]", а во всех остальных случаях, в том числе и в инверсном наборе работает без экранирования и в любом месте набора - хоть "[abc^tre]" хоть "[^abc^ert]" Пример повторяет предыдущий:
Расшифровка шаблона: "([^|]+)" - группа с захватом, внутри которой повтор символа набора минимум 1 раз, к символам набора относится всё, кроме символа "|".
"[:class:]" - любой символ класса. Работает как обычный набор, в который включены определенные символы, например, [:digit:] - цифровые символы как и "\d". Есть множество разных классов, можете рассмотреть их самостоятельно в справке.
"[^:class:]" - не символ класса. Аналогичен "ни один символ из набора".
"|" - логическое ИЛИ выбора между символами, группами или наборами. Выбор может осуществляться между двумя и более элементами/группами/наборами. Пример выбирает из ответов, только ответы а, в и г:
Расшифровка шаблона: "(?:а|в|г)" - группа без захвата, в которую входит один из трех элементов "\)" - экранированная скобка (.*?) - любое количество любых символов с минимальной жадностью "\." - экранированная точка Более намудренный пример - выводит месяц и число, при условии что они оба встретились до точки:
Расшифровка шаблона: знак "|" разделяет два возможных варианта, оба варианта сделаны без захвата, чтобы избежать дублей в выводе. "(?:\d+\s.*?)" - группа без захвата: цифровой символ, повторенный 1 или более раз, пробел и любой символ, повторенный любое количество раз с минимальной жадностью, т.е. до точки в конце - которая стоит за шаблоном. "(?:[^\s]+\s\d+)" - группа без захвата: любой символ кроме пробела, повторенный 1 или более раз, пробел и цифровой символ, повторенный минимум 1 раз, здесь нет необходимости минимизировать жадность, т.к. точка не может войти в "\d". "\." - экранированная точка вынесена за пределы групп, чтобы не попасть в них.
"(?# )" - комментарий к шаблону, после "#" и до закрывающей скобки может быть любой комментарий. Пример:
Сделал разделение для лучшего восприятия информации. Здесь привожу специальные сочетания символов для обозначения каких-либо условий, положений в тексте, символов и т.п. Таких сочетаний очень много, разберу наиболее популярные из них.
"\A" - начало строки, не зависит от флага "(?m)" и поэтому может встретится только 1 раз. Примеры см. в "$".
"\b" - граница слова (например, буква "b" и "g" в слове "boring" находятся на границе, а буквы "orin" нет). Работает только для букв английского алфавита! Пример показывает замену слова "123" на "[замена]", при этом другие слова, которые так же содержат данное сочетания не будут задеты.
"\B" - не граница слова. Пример показывает замену сочетания "123" на "[замена]"в словах, где это сочетание находится не на границе слова с обоих сторон:
"\D" - любая не цифра, т.е. всё что не попадает в интервал от 0 до 9 (буквы пробелы и т.п.).
"\E" - окончание действия метасимвола "\Q" (см. ниже)
"\n" - символ новой строки или перевода строки. В AutoIt это @LF. Пример показывает разбиение текста на слова по символам перевода строки, при включенном флаге "(?s)" (рассмотрим ниже), когда "." может "съедать" переводы строк:
Расшифровка шаблона: "(?s)" - особый режим для "." "([^\n]+)" - группа с захватом: любой символ, кроме перевода строки в количестве не менее 1 штуки.
"\Q" - отключение действия метасимволов до появления "\E", т.е. все символы метасимволы, заключенные между "\Q" и "\E" будут экранированы, как при использовании "\".
"\r" - символ возврата каретки в AutoIt это @CR.
"\s" - символ пробела, совпадающий с: обычным пробелом, горизонтальная табуляция, вертикальная табуляция, началом строки, возвратом каретки или началом новой страницы "( \r\n\v\t\f\h)"
"\S" - не символ пробела. Т.е. фактически любой видимый при печати символ (пробелы невидимы )
"\t" - символ горизонтальной табуляции. В AutoIt это @TAB
"\v" - любой вертикальный "пробел" к таким относятся : @CR, @LF, вертикальная табуляция (устарела) и "\f" - символ конца страницы (его мы не будем рассматривать) Пример наглядно показывает, что попадает под символ "\v" с помощью получения ASCI-кодов захваченных символов:
Код
#include <Array.au3> $sText = 'Anna' & @LF & 'Maria' & @CRLF & 'Alice' & @CR & 'Kate' $sPattern = '(?s)(\v+)' $aResult = StringRegExp($sText, $sPattern, 3) For $i = 0 To UBound($aResult) - 1 $sTemp = '' For $j = 1 To StringLen($aResult[$i]) $sTemp &= Asc(StringMid($aResult[$i], $j, 1)) & ' | ' Next $aResult[$i] = $sTemp Next _ArrayDisplay($aResult)
"\w" - символ буквы "слова", соответствует "(a-zA-Z0-9_)" Пример делит текст по символам, не входящим в "\w":
"\W" - не символ буквы "слова" - все что не попало в "(a-zA-Z0-9_)", т.е. русские буквы, символы пробелов, символы пунктуации и пр.
"\z" - совпадает с концом строки, не зависит от флага "(?m)" (будет рассмотрен ниже).
"\Z" - совпадает с концом строки или позицией до последнего символа новой строки, не зависит от флага "(?m)" (будет рассмотрен ниже). "\z" и "\Z" очень похожи, различие есть только в ситуации, когда в самом конце текста стоит @LF, тогда при "\Z" RegExp встанет на месте до @LF, а при "\z" RegExp встанет в самый конце текста.
Код
#include <Array.au3> $sText = 'aaaa' & @LF $sPattern = '(a+)\Z' ; замените \Z на \z и результата не будет, т.к. RegExp будет пытаться найти символ "a" сразу перед концом строки, а там находится @LF ; \Z же ищет такой символ перед @LF и находит $aResult = StringRegExp($sText, $sPattern, 3) _ArrayDisplay($aResult)
Метасимволы. Символы повтора или квантификаторы
Квантификатор относится только к символу, набору или группе, после которого он написан.
"*" - повторить предыдущий символ (группу) 0 или более раз. Т.е. символа может и не быть.
"+" - повторить предыдущий символ (группу) 1 или более раз. Т.е. существует минимум 1 такой символ.
"{x}" - повторить предыдущий символ (группу) ровно "x" раз. "\d{5}" соответствует пятикратному повтору цифрового символа (12345 55555 00000).
"{x,}" - повторить предыдущий символ (группу) минимум "x" раз. "\d{3,}" соответствует трехкратному и более повтору цифрового символа (123 1234 00000).
"{0,y}" - повторить предыдущий символ (группу) максимум "y" раз. "\d{0,5}" соответствует пятикратному и менее повтору цифрового символа ("отсутствие цифры" 1 12 123 1234 12345), будьте осторожны - данный шаблон может возвращать отсутствие цифр как таковых, т.к. они соответствуют шаблону"\d{0}".
"{x, y}" - повторить предыдущий символ (группу) от "x" до "y" раз. "\d{3,5}" соответствует повтору цифрового символа от трех до пяти раз (123 1234 12345).
"?" - предыдущий символ (группа) может быть или отсутствует. "?div>" соответствует тэгу "
" и "
". Если знак "?" указан после символа повтора, то он будет выбирать меньшее количество символов вместо большего. Т.е. повтор станет менее жадным. Пример показывает, как работает "?" с символом повтора. В первом случае под группу "(.*)" попадает слово, пробел и первый символ цифры, и только вт