Нет, сегодня речь не пойдет о вооруженных людях в черных масках 😉 Я просто продолжаю свой рассказ об основах регулярных выражениях, и сегодня мы рассмотрим одну из самых интересных и полезных их возможностей.
Если вы натолкнулись на этот пост случайно, сначала лучше прочитайте предыдущие посты из этой серии:
Теперь, когда мы можем с помощью регулярных выражений описывать и проверять строки по достаточно сложным правилам, пора познакомится с другой не менее важной концепцией регулярных выражений — "группами захвата" (capture groups). Как следует из названия, группы можно использовать для … сюрприз … группировки 🙂 К группам захвата, как и к символам и символьным группам можно применять количественные модификаторы. Например следующее выражение означает "Первая буква в строке — S, затем одна или больше групп состоящих из “знака -
(минус) и любого количества цифр за ним” до конца строки":
PS C:\> "S-1-5-21-1964843605-2840444903-4043112481" -match "^S(-\d+)+$" True Или: PS C:\> "Ноут","Ноутбук","Лептоп" -match "Ноут(бук)?" Ноут Ноутбук
Эти примеры показывают как можно использовать группы захвата для группировки, но это вовсе не главное их качество 🙂 Гораздо важнее то, что часть строки подпавшая под подвыражение находящееся внутри такой группы, помещается в специальную переменную — $matches
. $Matches
это массив, и в нем может находится содержимое нескольких групп. Причем под индексом 0 туда помещается вся совпавшая строка, а лишь начиная с единицы идет содержимое групп захвата. Опять же, лучше посмотреть на примере:
PS C:\> "At 17:04 Firewall service was stopped." -match "(\d\d:\d\d) (\S+)" True PS C:\> $matches Name Value ---- ----- 2 Firewall 1 17:04 0 17:04 Firewall
Под индексом 0 находится вся часть строки подпавшая под регулярное выражение, под 1 находится содержимое первых скобок, и под 2 соответственно содержимое вторых скобок. К содержимому $matches
можно обращаться как к элементам любого другого массива в PowerShell:
PS C:\> $matches[1] 17:04 PS C:\> $matches[2] Firewall
Если в строке присутствует много групп захвата, то бывает полезно дать им имена, это сильно облегчает дальнейшую работу с полученными данными:
PS C:\> "At 17:04 Firewall service was stopped." -match "(?<Время>\d\d:\d\d) (?<Служба>\S+)" True PS C:\> $matches Name Value ---- ----- Время 17:04 Служба Firewall 0 17:04 Firewall PS C:\> $matches.Время 17:04 PS C:\> $matches["Служба"] Firewall
Регулярное выражение конечно усложнилось, но зато работать с результатами гораздо приятнее. Синтаксис именования следующий:
(?<Название Группы>подвыражение)
Не перепутайте порядок, сначала следует знак вопроса. Как вы помните, количественные модификаторы, в том числе ?
могут применяться только после группы, и следовательно в начале подвыражения — бессмысленны. Поэтому в группах, знак вопроса следующий сразу за открывающей скобкой, означает особый тип группы, в нашем примере — именованную.
Другой тип группы который часто используется — незахватывающая группа. Она может пригодиться в тех случаях когда вам не нужно захватывать содержимое группы, а надо применить её только для группировки. Например в вышеприведённом примере с SID, такая группа была бы более уместна:
PS C:\> "S-1-5-21-1964843605-2840444903-4043112481" -match "^S(?:-\d+)+$" True PS C:\> $matches Name Value ---- ----- 0 S-1-5-21-1964843605-2840444903-4043112481
Как вы могли заметить, синтаксис такой группы: (?:подвыражение)
. Группы можно и вкладывать одну в другую:
PS C:\> "MAC address is '00-19-D2-73-77-6F'." -match "is '([a-f\d]{2}(?:-[a-f\d]{2}){5})'" True PS C:\> $matches Name Value ---- ----- 1 00-19-D2-73-77-6F 0 is '00-19-D2-73-77-6F'
Попробуйте разобрать данное выражение самостоятельно 🙂
Продолжение: Операторы -replace и -split
11.12.2009 в 18:26
[…] Группы захвата Опубликовано в Regular Expressions. Метки: Regular Expressions. […]
14.12.2009 в 11:27
Спасибо за интересный материал, о $matches даже не догадывался честно говоря.
14.12.2009 в 12:35
Ну догадаться до такого сложновато 🙂 В PS много ньюансов 🙂
14.12.2009 в 12:11
«Первая буква в строке – S, затем одна или больше групп состоящих из (минус и любое количество цифр) до конца строки»:
Приветы. 😉
Может быть стои заменить в этой фразе «минус» на «знака минус» (или чего-либо подобного)? Долго не мог въехать в смысл этой фразы — казалось, что в предложении по ошибке пропущенно некое существительное, из которого надо вычесть любое количество цифр 😉
14.12.2009 в 12:34
Да, действительно, сейчас заменю и еще в кавычки засуну всё.
Делитесь такими поправками плиз, я посты писал урывками, там наверняка много описок и бессмыслицы 🙂
16.12.2009 в 16:53
Спасибо за статью.
Тоже долго искал где же сохраняются найденные группы.
Почему-то ожидал встретить нечто подобное ruby. (Раз уж в Powershell все есть объект). Например
$result = («foo bar» -match ‘(foo)\s+(bar)’)
Было бы логично если бы $result был объектом. И тогда сразу
$result[2] -> строка »bar»
Или сразу:
(«foo bar» -match ‘(foo)\s+(bar)’)[2]
P.S. Кстати, исправьте в тексте «…а лишь начиная с еденицы …»
(едИница)
16.12.2009 в 17:34
Виктор, такое тоже есть 🙂 В PowerShell кроме того что всё в объектах, еще важно удобство пользования. И $matches с этой точки зрения лучше и проще. Зато работа напрямую с объектами регулярных выражений даёт больше возможностей, так что обязательно расскажу и о них позже.
18.12.2009 в 12:52
[…] Группы захвата […]
24.12.2009 в 12:04
[…] Регулярные выражения – Жадность 24.12.2009 — Xaegr Просто отличное название для очередной статьи о регулярных выражениях в блоге посвященном PowerShell Но оно действительно подходит лучше всего. Сегодня мы поговорим об одной важной концепции регулярных выражений. От чего зависит сколько символов будет захвачено количественным модификатором с варьирующейся длинной? Именно от жадности Если вы наткнулись на пост случайно, то сначала лучше ознакомьтесь с предыдущими постами серии – 1,2,3,4,5. […]
28.12.2009 в 12:10
[…] отрицательными группами и якорями, квантификаторами, группами захвата, операторами –replace и –split, а так же с концепцией […]
24.2.2010 в 11:00
[…] посты которые рекомендуется прочитать сначала: 1, 2, 3, 4, 5, 6, […]
17.3.2010 в 15:36
После выполнения «S-1-5-21-1964843605-2840444903-4043112481» -match «^S(-\d+)+$»
$matches будет иметь следующее содержимое:
Name Value
—- ——
1 -4043112481
0 S-1-5-21-1964843605-2840444903-4043112481
а можно ли каким-либо способом добиться того, чтобы сохранить для последующей обработки все найденые для (-\d+) совпадения в строке (т.е. -1, -5, -21, -1964843605, и т.д.)?
17.3.2010 в 19:52
Да! Об этом будет в следующем посте из серии 🙂 К сожалению пока не знаю когда до него доберусь 😦 Но он будет точно.
17.3.2010 в 19:53
Вот пример если прям срочно 🙂 — https://xaegr.wordpress.com/2009/10/31/extract-webpage-links/
18.3.2010 в 17:30
Спасибо! А то я уже весь хелп носом изрыл. А оказывается решили не не заморачиваться и не дублировать то, что есть в .Net (а, вот, .Net мне практически не знаком 😦 ).
18.3.2010 в 17:38
Да нет, по сути половина PowerShell — дублирование .NET 🙂 Просто видимо посчитали что такой advanced функционал не в первую очередь надо упрощать. Те гуры которым надо — и через .NET доберутся 🙂
6.4.2010 в 10:23
[…] – нежадная группа захвата, которая захватит и сохранит для нас все символы, которые расположены […]