Этот сайт использует cookies. Продолжение работы с сайтом означает, что Вы согласны!
Регулярные выражения
Регулярные выражения предназначены для выполнения сложного поиска или замены в строке. В языке VBA нет встроенной поддержки регулярных выражений, однако можно получить доступ через объект VBScript.RegExp
. Создать объект позволяет следующий код:
Dim re
Set re = CreateObject("VBScript.RegExp")
Синтаксис регулярных выражений
После создания объекта нужно задать значения следующим свойствам:
Pattern
— устанавливает или возвращает шаблон;IgnoreCase
— если указано значениеTrue
, то поиск не будет зависить от регистра символов, а еслиFalse
(значение по умолчанию) — то будет:
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "^[а-яё]+$" ' Задаем шаблон
re.IgnoreCase = False ' Зависит от регистра
Debug.Print re.Test("АБВГДЕЁ") ' False (не совпадает)
re.IgnoreCase = True ' Не зависит от регистра
Debug.Print re.Test("АБВГДЕЁ") ' True (совпадает)
Global
— если указано значениеTrue
, то будут искаться все совпадения с шаблоном (глобальный поиск), а еслиFalse
(значение по умолчанию) — то только первое совпадение:
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "[0-9]+"
re.Global = False
Debug.Print re.Replace("12 5 45", "+") ' + 5 45
re.Global = True ' Глобальный поиск
Debug.Print re.Replace("12 5 45", "+") ' + + +
MultiLine
— если указано значениеTrue
, то используется многострочный режим, а еслиFalse
(значение по умолчанию) — то однострочный. При многострочном режиме производится поиск в строке, состоящей из нескольких подстрок, разделенных символом новой строки ("\n"
). Символ^
соответствует привязке к началу каждой подстроки, а символ$
соответствует позиции перед символом перевода строки.
Шаблон может содержать комбинации следующих символов, имеющих специальное значение:
\n
— перевод строки;\r
— возврат каретки;\t
— знак табуляции;\v
— вертикальная табуляция;\f
— перевод формата;\N
— восьмеричное значениеN
. Например,\74
соответствует символу<
;\xN
— шестнадцатеричное значениеN
. Например,\x6a
соответствует символуj
;\uxxxx
— символ Unicode. Например,\u043a
соответствует русской буквек
;\cN
— специальное значениеN
.\cJ
— перевод строки,\cM
— возврат каретки,\cI
— знак табуляции,\cK
— вертикальная табуляция,\cL
— перевод формата.
Внутри шаблона символы .
, ^
, $
, *
, +
, ?
, {
, [
, ]
, \
, |
, (
и )
имеют специальное значение. Если эти символы должны трактоваться как есть, то их следует экранировать с помощью слеша. Некоторые специальные символы теряют свое специальное значение, если их разместить внутри квадратных скобок. В этом случае экранировать их не нужно. Например, метасимвол "точка" соответствует любому символу, кроме символа перевода строки. Если необходимо найти именно точку, то перед точкой необходимо указать символ \
или разместить точку внутри квадратных скобок ([.]
). Продемонстрируем это на примере проверки правильности введенной даты (листинг 7.1).
Листинг 7.1. Проверка правильности ввода даты
Dim re, d As String
Set re = CreateObject("VBScript.RegExp")
d = "29,12.2012" ' Вместо точки указана запятая
re.Pattern = "^[0-3][0-9].[01][0-9].[12][09][0-9][0-9]$"
' Символ "\" не указан перед точкой
If re.Test(d) = True Then
Debug.Print "Дата введена правильно"
Else
Debug.Print "Дата введена неправильно"
End If
' Так как точка означает любой символ,
' выведет: Дата введена правильно
re.Pattern = "^[0-3][0-9]\.[01][0-9]\.[12][09][0-9][0-9]$"
' Символ "\" указан перед точкой
If re.Test(d) = True Then
Debug.Print "Дата введена правильно"
Else
Debug.Print "Дата введена неправильно"
End If
' Так как перед точкой указан символ "\",
' выведет: Дата введена неправильно
re.Pattern = "^[0-3][0-9][.][01][0-9][.][12][09][0-9][0-9]$"
' Точка внутри квадратных скобок
If re.Test(d) = True Then
Debug.Print "Дата введена правильно"
Else
Debug.Print "Дата введена неправильно"
End If
' Выведет: Дата введена неправильно
30.02.2012
является допустимой для шаблона, но в реальности не существует. В большинстве случаев проверки на совпадение с шаблоном достаточно, но если необходима проверка реальности существования даты, то следует воспользоваться функциями для работы с датами, которые мы рассмотрим в следующей главе.Метасимвол "точка" соответствует любому символу, кроме символа перевода строки. Чтобы обозначить любой символ, включая символ перевода строки, следует воспользоваться комбинаций [\s\S]
. Пример:
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "^.$"
Debug.Print re.Test(vbLf) ' False
re.Pattern = "^[\s\S]$"
Debug.Print re.Test(vbLf) ' True
В этом примере мы осуществляли привязку к началу и концу строки с помощью следующих метасимволов:
^
— привязка к началу строки или подстроки (зависит от значения свойстваMultiLine
);$
— привязка к концу строки или подстроки (зависит от значения свойстваMultiLine
).
Если свойство MultiLine
равно значению True
, то поиск производится в строке, состоящей из нескольких подстрок, разделенных символом новой строки (\n
). В этом случае символ ^
соответствует привязке к началу каждой подстроки, а символ $
соответствует позиции перед символом перевода строки (листинг 7.2).
Листинг 7.2. Пример использования многострочного режима
Dim re, Matches, Item, s As String
s = "123" & vbLf & "456"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "^.+$" ' Точка не соответствует \n
re.Global = True ' Глобальный поиск
re.MultiLine = False ' Однострочный режим
Debug.Print re.Test(s) ' False (Ничего не найдено)
re.MultiLine = True ' Многострочный режим
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Next
' 123
' 456
Привязку к началу и концу строки следует использовать, если строка должна полностью соответствовать регулярному выражению. Например, привязку нужно использовать для проверки, содержит ли строка число (листинг 7.3).
Листинг 7.3. Проверка наличия целого числа в строке
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "^[0-9]+$"
re.MultiLine = False ' Однострочный режим
Debug.Print re.Test("245") ' True
Debug.Print re.Test("Строка245") ' False
Если убрать привязку к началу и концу строки, то любая строка, содержащая хотя бы одну цифру, будет соответствовать шаблону (листинг 7.4).
Листинг 7.4. Отсутствие привязки к началу или концу строки
re.Pattern = "[0-9]+"
Debug.Print re.Test("Строка245") ' True
Можно указать привязку только к началу или только к концу строки (листинг 7.5).
Листинг 7.5. Привязка к началу и концу строки
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "[0-9]+$"
If re.Test("Строка245") = True Then
Debug.Print "Есть число в конце строки"
Else
Debug.Print "Нет числа в конце строки"
End If
' Выведет: Есть число в конце строки
re.Pattern = "^[0-9]+"
If re.Test("Строка245") = True Then
Debug.Print "Есть число в начале строки"
Else
Debug.Print "Нет числа в начале строки"
End If
' Выведет: Нет числа в начале строки
В квадратных скобках []
можно указать символы, которые могут встречаться на этом месте в строке. Можно перечислять символы подряд или указать диапазон через тире:
[09]
— соответствует числу 0 или 9;[0-9]
— соответствует любому числу от 0 до 9;[абв]
— соответствует буквам "а", "б" и "в";[а-г]
— соответствует буквам "а", "б", "в" и "г";[а-яё]
— соответствует любой букве от "а" до "я";[АБВ]
— соответствует буквам "А", "Б" и "В";[А-ЯЁ]
— соответствует любой букве от "А" до "Я";[а-яА-ЯёЁ]
— соответствует любой русской букве в любом регистре;[0-9а-яА-ЯёЁa-zA-Z]
— любая цифра и любая буква независимо от регистра и языка.
[а-я]
.Значение можно инвертировать, если после первой скобки указать символ ^
. Таким образом можно указать символы, которых не должно быть на этом месте в строке:
[^09]
— не цифра 0 или 9;[^0-9]
— не цифра от 0 до 9;[^а-яА-ЯёЁa-zA-Z]
— не буква.
Как вы уже знаете, точка теряет свое специальное значение, если ее заключить в квадратные скобки. Кроме того, внутри квадратных скобок могут встретиться символы, которые имеют специальное значение (например, ^
и -
). Символ ^
теряет свое специальное значение, если он не расположен сразу после открывающей квадратной скобки. Чтобы отменить специальное значение символа -
, его необходимо указать после перечисления всех символов, перед закрывающей квадратной скобкой, или сразу после открывающей квадратной скобки. Все специальные символы можно сделать обычными, если перед ними указать символ \
.
Вместо перечисления символов можно использовать стандартные классы:
\d
— соответствует любой цифре; эквивалентно[0-9]
;\D
— не цифра; эквивалентно[^0-9]
;\w
— соответствует любой латинской букве, цифре или символу подчеркивания; эквивалентно[a-zA-Z0-9_]
;\W
— не буква, не цифра и не символ подчеркивания; эквивалентно[^a-zA-Z0-9_]
;\s
— любой пробельный символ; эквивалентно[ \t\n\r\f\v]
;\S
— не пробельный символ; эквивалентно[^ \t\n\r\f\v]
.
Количество вхождений символа в строку задается с помощью квантификаторов:
{n}
—n
вхождений символа в строку. Например, шаблон"^[0-9]{2}$"
соответствует двум вхождениям любой цифры;{n,}
—n
или более вхождений символа в строку. Например, шаблон"^[0-9]{2,}$"
соответствует двум и более вхождениям любой цифры;{n,m}
— не менееn
и не болееm
вхождений символа в строку. Числа указываются через запятую без пробела. Например, шаблон"^[0-9]{2,4}$"
соответствует от двух до четырех вхождениям любой цифры;*
— ноль или большее число вхождений символа в строку. Эквивалентно комбинации{0,}
;+
— одно или большее число вхождений символа в строку. Эквивалентно комбинации{1,}
;?
— ни одного или одно вхождение символа в строку. Эквивалентно комбинации{0,1}
.
Все квантификаторы являются "жадными". При поиске соответствия ищется самая длинная подстрока, соответствующая шаблону, и не учитываются более короткие соответствия. Рассмотрим это на примере. Получим содержимое всех тегов <b>
, вместе с тегами:
Dim re, Matches, Item, s As String
s = "<b>Text1</b>Text2<b>Text3</b>"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "<b>.*</b>"
re.Global = True ' Глобальный поиск
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Next
' <b>Text1</b>Text2<b>Text3</b>
Вместо желаемого результата мы получили полностью строку. Чтобы ограничить "жадность", необходимо после квантификатора указать символ ?
(листинг 7.6).
Листинг 7.6. Ограничение жадности квантификаторов
Dim re, Matches, Item, s As String
s = "<b>Text1</b>Text2<b>Text3</b>"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "<b>.*?</b>"
re.Global = True ' Глобальный поиск
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Next
' <b>Text1</b>
' <b>Text3</b>
Этот код вывел то, что мы искали. Если необходимо получить содержимое без тегов, то нужный фрагмент внутри шаблона следует разместить внутри круглых скобок (листинг 7.7). В этом случае фрагмент будет доступен через коллекцию Submatches
.
Листинг 7.7. Получение значения определенного фрагмента
Dim re, Matches, Submatches, Item, SubItem, s As String
s = "<b>Text1</b>Text2<b>Text3</b>"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "<b>(.*?)</b>"
re.Global = True ' Глобальный поиск
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Set Submatches = Item.Submatches
For Each SubItem In Submatches
Debug.Print " " & SubItem
Next
Next
' <b>Text1</b>
' Text1
' <b>Text3</b>
' Text3
Круглые скобки часто используются для группировки фрагментов внутри шаблона. В этом случае не требуется, чтобы фрагмент запоминался и был доступен в результатах поиска. Чтобы избежать захвата фрагмента после открывающей круглой скобки следует разместить символы ?:
(листинг 7.8).
Листинг 7.8. Ограничение захвата фрагмента
Dim re, Matches, Submatches, Item, SubItem, s As String
s = "test text"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "([a-z]+((st)|(xt)))"
re.Global = True ' Глобальный поиск
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Set Submatches = Item.Submatches
For Each SubItem In Submatches
Debug.Print " " & SubItem
Next
Next
' test
' test
' st
' st
'
' text
' text
' xt
'
' xt
re.Pattern = "([a-z]+(?:(?:st)|(?:xt)))"
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Set Submatches = Item.Submatches
For Each SubItem In Submatches
Debug.Print " " & SubItem
Next
Next
' test
' test
' text
' text
В первом примере мы получили два фрагмента полностью соответствующих шаблону и по четыре субфрагмента, которые соответствуют фрагментам, заключенным в шаблоне в круглые скобки. Три последних субфрагмента являются лишними. Чтобы они не выводились в результатах, мы добавили символы ?:
после каждой открывающей круглой скобкой. Теперь результат состоит только из фрагментов, полностью соответствующих регулярному выражению.
Обратите внимание на регулярное выражение в предыдущем примере:
"([a-z]+((st)|(xt)))"
Здесь мы использовали метасимвол |
, который позволяет сделать выбор между альтернативными значениями. Выражение n|m
соответствует одному из символов: n
или m
. Пример:
красн((ая)|(ое)) — красная или красное, но не красный.
К найденному фрагменту в круглых скобках внутри шаблона можно обратиться с помощью механизма обратных ссылок. Для этого порядковый номер круглых скобок в шаблоне указывается после слеша, например, \1
. Нумерация скобок внутри шаблона начинается с 1
. Для примера получим текст между одинаковыми парными тегами (листинг 7.9).
Листинг 7.9. Обратные ссылки
Dim re, Matches, Submatches, Item, SubItem, s As String
s = "<b>Text1</b>Text2<I>Text3</I><b>Text4</b>"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "<([a-zA-Z]+)>(.*?)</\1>"
re.Global = True
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Set Submatches = Item.Submatches
For Each SubItem In Submatches
Debug.Print " " & SubItem
Next
Next
' <b>Text1</b>
' b
' Text1
' <I>Text3</I>
' I
' Text3
' <b>Text4</b>
' b
' Text4
Внутри круглых скобок могут быть расположены следующие конструкции:
(?=...)
— положительный просмотр вперед. Выведем все слова, после которых расположена запятая:
Dim re, Matches, Item, s As String
s = "text1, text2, text3 text4"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "\w+(?=[,])"
re.Global = True
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Next
' text1
' text2
(?!...)
— отрицательный просмотр вперед. Выведем все слова, после которых нет запятой:
Dim re, Matches, Item, s As String
s = "text1, text2, text3 text4"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "[a-z]+[0-9](?![,])"
re.Global = True
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Next
' text3
' text4
Рассмотрим небольшой пример. Предположим, необходимо получить все слова, расположенные после тире, причем перед тире и после слов должны следовать пробельные символы:
Dim re, Matches, Item, s As String
s = "-word1 -word2 -word3 -word4 -word5"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "\s\-([a-z0-9]+)\s"
re.Global = True
Set Matches = re.Execute(s)
For Each Item In Matches
Debug.Print Item.Value
Next
' -word2
' -word4
Как видно из примера, мы получили только два слова вместо пяти. Первое и последнее слово не попали в результат, т. к. расположены в начале и конце строки. Чтобы эти слова попали в результат, необходимо добавить альтернативный выбор (^|\s)
для начала строки и (\s|$)
для конца строки. Чтобы найденные выражения внутри круглых скобок не попали в результат, следует добавить символы ?:
после открывающей скобки:
re.Pattern = "(?:^|\s)\-([a-z0-9]+)(?:\s|$)"
' -word1
' -word3
' -word5
Первое и последнее слово успешно попали в результат. Почему же слова "word2" и "word4" не попали? Ведь перед тире есть пробел и после слова есть пробел. Чтобы понять причину, рассмотрим поиск по шагам. Первое слово успешно попадает в результат, т. к. перед тире расположено начало строки и после слова есть пробел. После поиска указатель перемещается, и строка для дальнейшего поиска примет следующий вид:
"-word1 <Указатель>-word2 -word3 -word4 -word5"
Обратите внимание на то, что перед фрагментом "-word2"
больше нет пробела и тире не расположено вначале строки. Поэтому следующим совпадением будет слово "word3", и указатель снова будет перемещен:
"-word1 -word2 -word3 <Указатель>-word4 -word5"
Опять перед фрагментом "-word4"
нет пробела и тире не расположено вначале строки. Поэтому следующим совпадением будет слово "word5" и поиск будет завершен. Таким образом, слова "word2" и "word4" не попадают в результат, т. к. пробел до фрагмента уже был использован в предыдущем поиске. Чтобы этого избежать следует воспользоваться положительным просмотром вперед (?=...)
:
Dim re, Matches, Submatches, Item, SubItem, s As String
s = "-word1 -word2 -word3 -word4 -word5"
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "(?:^|\s)\-([a-z0-9]+)(?=\s|$)"
re.Global = True
Set Matches = re.Execute(s)
For Each Item In Matches
Set Submatches = Item.Submatches
For Each SubItem In Submatches
Debug.Print SubItem
Next
Next
' word1
' word2
' word3
' word4
' word5
В этом примере мы заменили фрагмент (?:\s|$)
на (?=\s|$)
. Поэтому все слова успешно попали в результат.
Помощь сайту
Yandex-деньги: 410011140483022
ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов