Урок 2. Шаблоны

Автор Redline


Урок 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' & @LF & 'apr may jun' & @LF & 'jul aug sep' & @LF & 'oct nov dec'
$sPattern = '(?m)^(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-го символа строки:

#include <Array.au3>
$sText = '1234567890'
$sPattern = '^....(.*)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


"$" - совпадает с концом строки (для мультистрокового текста совпадает с концом каждой строки, но для этого необходимо включить спец флаг "(?m)", его рассмотрим позже)
Пример выводит весь текст, за исключением последних 4-го символов строки:

#include <Array.au3>
$sText = '1234567890'
$sPattern = '(.*)....$'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Пример с мультистроковым текстом выводит все слова, находящиеся в конце строки с "!" на конце:

#include <Array.au3>
$sText = 'ok! yes!' & @LF & 'no! !ok' & @LF & 'no!' & @LF & '!close' & @LF & 'open!'
$sPattern = '(?m)\s(.+!)$'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


"\" - символ экранирования, используется для отмены действия метасимвола, в случаях когда вам нужно использовать метасимвол как обычный литеральный.
Пример показывает обработку строки с использованием экранирования ")" и ".":

#include <Array.au3>
$sText = 'Кто убил Лору Палмер? Варианты ответа: а)бомж. б)китаец. в)Девид Линч. г)отец.'
$sPattern = '\)(.*?)\.'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


"( )" - групповой шаблон с захватом в память, в котором все элементы идут в указанном порядке. К группе могут применяться символы повтора.
Пример выводит имя и все цифровые параметры после него:

#include <Array.au3>
$sText = 'Jane 22 175 90 60 90 Marta 18 165 85 55 70 Alice 20 170 88 62 88'
$sPattern = '\s?(.*?(?:\s\d+)+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Расшифровка шаблона:
"\s?" - пробел в количестве 0 или 1 штуки
"(.*?" - начало группы с захватом: любой символ в любом количестве, но знак "?" не дает "съедать" строку дальше
"(?:\s\d+)+" - группа без захвата (будет рассмотрено дальше): пробел, после которого идет минимум один цифровой символ, вся группа повторяется минимум 1 раз
")" - собственно конец группы с захватом
"(?: )" - групповой шаблон без захвата. Ничем не отличается от предыдущего, но не сохраняется для дальнейшего вывода.
"[ ]" - любой символ из набора. Для внесения в набор непрерывных сочетаний, например, от "щ" до "я" используется символ дефиса "[щ-я]", если необходимо внести сам дефис в набор, его пишут в начале/конце набора или экранируют, кроме того нужно экранировать "]", "[", "\" и "^". Все метасимволы, попавшие в набор теряют свои свойства и становятся обычными символами.
Пример выводит результат разбиения строки по символу "|"

#include <Array.au3>
$sText = 'Берлин Германия|Афины Греция|Москва Россия'
$sPattern = '([а-яА-Я\s]+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Расшифровка шаблона:
"([а-яА-Я\s]+)" - группа с захватом, внутри которой повтор символа набора минимум 1 раз, к символам набора относятся: строчные и прописные русские буквы от а до я и символ пробела. Поскольку в наборе нет символа "|", то на нем данная группа прервется и начнет поиск дальше.

Буква "ё"


В набор "а-я" и "А-Я" не попадают буквы "ё" и "Ё" соответственно.
Их нужно дописывать в набор отдельно, либо использовать такие диапазоны: "а-яё" для строчных букв и "А-ЯЁ" для прописных.
Следовательно полный набор строчных русских букв попадает под шаблон "[а-яё]", полный набор прописных русских букв попадает под шаблон "[А-ЯЁ]", а совмещение этих шаблонов выглядит так: "[А-яЁё]" или "[А-Яа-яЁё]".
Пример:

#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]"
Пример повторяет предыдущий:

#include <Array.au3>
$sText = 'Берлин Германия|Афины Греция|Москва Россия'
$sPattern = '([^|]+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Расшифровка шаблона:
"([^|]+)" - группа с захватом, внутри которой повтор символа набора минимум 1 раз, к символам набора относится всё, кроме символа "|".
"[:class:]" - любой символ класса. Работает как обычный набор, в который включены определенные символы, например, [:digit:] - цифровые символы как и "\d". Есть множество разных классов, можете рассмотреть их самостоятельно в справке.
"[^:class:]" - не символ класса. Аналогичен "ни один символ из набора".
"|" - логическое ИЛИ выбора между символами, группами или наборами. Выбор может осуществляться между двумя и более элементами/группами/наборами.
Пример выбирает из ответов, только ответы а, в и г:

#include <Array.au3>
$sText = 'Кто убил Лору Палмер? Варианты ответа: а)бомж. б)китаец. в)Девид Линч. г)отец.'
$sPattern = '(?:а|в|г)\)(.*?)\.' ; альтернатива '[авг]\)(.*?)\.'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Расшифровка шаблона:
"(?:а|в|г)" - группа без захвата, в которую входит один из трех элементов
"\)" - экранированная скобка
(.*?) - любое количество любых символов с минимальной жадностью
"\." - экранированная точка
Более намудренный пример :) - выводит месяц и число, при условии что они оба встретились до точки:

#include <Array.au3>
$sText = '15 July. April 21. 24 Feb. 14 Jan. 20110115. Feb'
$sPattern = '((?:\d+\s.*?)|(?:[^\s]+\s\d+))\.'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Расшифровка шаблона:
знак "|" разделяет два возможных варианта, оба варианта сделаны без захвата, чтобы избежать дублей в выводе.
"(?:\d+\s.*?)" - группа без захвата: цифровой символ, повторенный 1 или более раз, пробел и любой символ, повторенный любое количество раз с минимальной жадностью, т.е. до точки в конце - которая стоит за шаблоном.
"(?:[^\s]+\s\d+)" - группа без захвата: любой символ кроме пробела, повторенный 1 или более раз, пробел и цифровой символ, повторенный минимум 1 раз, здесь нет необходимости минимизировать жадность, т.к. точка не может войти в "\d".
"\." - экранированная точка вынесена за пределы групп, чтобы не попасть в них.
"(?# )" - комментарий к шаблону, после "#" и до закрывающей скобки может быть любой комментарий.
Пример:

#include <Array.au3>
$sText = '123 aaa 123 aaa 123 aaa'
$sPattern = '(\d+)(?#очень умный комментарий)\s?(a+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Метасимволы. Дополнительные


Сделал разделение для лучшего восприятия информации. Здесь привожу специальные сочетания символов для обозначения каких-либо условий, положений в тексте, символов и т.п. Таких сочетаний очень много, разберу наиболее популярные из них.
"\A" - начало строки, не зависит от флага "(?m)" и поэтому может встретится только 1 раз. Примеры см. в "$".
"\b" - граница слова (например, буква "b" и "g" в слове "boring" находятся на границе, а буквы "orin" нет). Работает только для букв английского алфавита!
Пример показывает замену слова "123" на "[замена]", при этом другие слова, которые так же содержат данное сочетания не будут задеты.

$sText = '1234 01234 123 0123'
$sPattern = '\b123\b'
$sResult = StringRegExpReplace($sText, $sPattern, '[замена]')
ConsoleWrite($sResult & @CRLF)


"\B" - не граница слова.
Пример показывает замену сочетания "123" на "[замена]"в словах, где это сочетание находится не на границе слова с обоих сторон:

$sText = '1234 01234 123 0123'
$sPattern = '\B123\B'
$sResult = StringRegExpReplace($sText, $sPattern, '[замена]')
ConsoleWrite($sResult & @CRLF)


"\d" - любая цифра от 0 до 9.
"\D" - любая не цифра, т.е. всё что не попадает в интервал от 0 до 9 (буквы пробелы и т.п.).
"\E" - окончание действия метасимвола "\Q" (см. ниже)
"\n" - символ новой строки или перевода строки. В AutoIt это @LF.
Пример показывает разбиение текста на слова по символам перевода строки, при включенном флаге "(?s)" (рассмотрим ниже), когда "." может "съедать" переводы строк:

#include <Array.au3>
$sText = 'Anna' & @LF & 'Maria' & @LF & 'Alice'
$sPattern = '(?s)([^\n]+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Расшифровка шаблона:
"(?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":

#include <Array.au3>
$sText = 'Anna' & @LF & 'Maria' & @CRLF & 'Alice' & @CR & 'Kate'
$sPattern = '(?s)([\w]+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


"\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>" соответствует тэгу "<div>" и "</div>".
Если знак "?" указан после символа повтора, то он будет выбирать меньшее количество символов вместо большего. Т.е. повтор станет менее жадным.
Пример показывает, как работает "?" с символом повтора. В первом случае под группу "(.*)" попадает слово, пробел и первый символ цифры, и только вторая цифра будет отдана под "(\d+)". Во втором уже измененная группа "(.*?)" "съест" только слово и пробел, а все цифры достанутся группе "(\d+)".

#include <Array.au3>
$sText = 'удав 11'
$sPattern = '(.*)(\d+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)

#include <Array.au3>
$sText = 'удав 11'
$sPattern = '(.*?)(\d+)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)



Метасимволы. Флаги


Для включения специальных режимов обработки RegExp применяются флаги.
"(?i)" По умолчанию RegExp чувствителен к регистру. "(?i)" - выключает чувствительность RegExp к регистру обрабатываемого текста.
В примере показано как при включенном флаге "(?i)" получено совпадение со строкой, которая отличатся от шаблона регистром написания.

$sText = 'Marta'
$sPattern = '(?i)marta'
If StringRegExp($sText, $sPattern) Then ConsoleWrite('Совпадение!' & @CRLF)


К сожалению данный флаг не реагирует на русские буквы, т.е. для русских букв этот флаг всегда выключен и регистр всегда имеет значение. Обходные решения для данного вопроса можете посмотреть здесь - Поиск без учета регистра. AutoIt не распознает русский язык в рег. выражениях
Флаг "(?i)" можно отключить в любом месте шаблона и включить назад:
"(?-i)" - включает чувствительность к регистру
Пример показывает переключение флагов внутри шаблона и выводит слова, начинающиеся с заглавных букв:

#include <Array.au3>
$sText = 'Marta start on Jane end of Alice is in'
$sPattern = '([A-Z](?i)[a-z]*)'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Флаги "(?i)" и "(?-i)" могут действовать внутри групп без захвата формат: "(?i:abc)" или "(?-i:abc)"
"(?s)" По умолчанию метасимвол "." может являться любым одиночным символом за исключением символа новой строки, т.е. действие этого спец. символа ограничено в пределах одной строки.
Для работы с многострочным текстом это не всегда удобно. "(?s)" - флаг, позволяющий метасимволу "." совпадать с символом новой строки.
Пример показывает, получение содержимого между тегами "<div>" и "</div>", без включения флага "(?s)" RegExp ничего не находит, т.к. группа с захватом "(.*)" захватит весь текст после "<div>" и до символа новой строки, после этого действие группы заканчивается и дальше сразу должен идти тег "</div>", но его там нет: шаблон не совпал.
А после включения флага "(?s)" группа "(.*)" захватывает все, пока не встретит тэг "</div>".

$sText = '<div>слон' & @CRLF & 'человек' & @CRLF & 'крокодил</div>'
ConsoleWrite($sText & @CRLF)
ConsoleWrite('!Шаблон №1:' & @CRLF)
$sPattern = '<div>(.*)</div>'
$aResult = StringRegExp($sText, $sPattern, 3)
If IsArray($aResult) Then
    ConsoleWrite($aResult[0] & @CRLF)
Else
    ConsoleWrite('Нет совпадения!' & @CRLF)
EndIf
ConsoleWrite('!Шаблон №2:' & @CRLF)
$sPattern = '(?s)<div>(.*)</div>'
$aResult = StringRegExp($sText, $sPattern, 3)
If IsArray($aResult) Then
    ConsoleWrite($aResult[0] & @CRLF)
Else
    ConsoleWrite('Нет совпадения!' & @CRLF)
EndIf


"(?U)" - инвертирует жадность квантификаторов (символов повтора). U Пишется в верхнем регистре!
По умолчанию квантификаторы выбирают больший фрагмент строки перед меньшим. Это можно изменить дописав после символа повтора знак "?".
Данный флаг отключает жадность для всех квантификаторов шаблона.
Флаг "(?U)" можно отключить в любом месте шаблона:
"(?-U)" - после этого квантификаторы снова станут жадными.
"(?m)" - многостроковый (multyline) режим работы с RegExp в котором символы "^" и "$" будут являться не началом и концом текста, а началом и концом произвольной строки текста (см. примеры с "^" и "$" ). Данный флаг отключаем:
"(?-m)" - отключить мультистроковый режим работы RegExp
"(?x)" - игнорировать все истинные пробелы и комментарии.
При включении этого флага пробелы в шаблоне вида, имеющие вид " ", а не "\s" будут проигнорированы, также будет проигнорировано все что идет после символа "#" (символа комментариев) до самого конца шаблона. Эти особенности употребляются для повышения читабельности шаблонов.
Пример:

#include <Array.au3>
$sText = '123 123 123'
$sPattern = '(?x)            (\d+)    #комментарием является все, что идет после # и до конца строки (\s+)$ <- эта конструкция тоже будет комментарием'
$aResult = StringRegExp($sText, $sPattern, 3)
_ArrayDisplay($aResult)


Для сокращенной записи используется совместное написание флагов:
"(?si)", "(?Ui)", "(?iUs)" и т.п. для включения
"(?-iU)", "(?-Um)", "(?-mi)" и т.п. для выключения
"(?i-m)", "(?is-U)", "(?-im)" и т.п. для одновременного включения одних и отключения других флагов.