Правильные (Regular) выражения и класс Regex. Операторы над компонентами регулярных выражений, страница 4

Он пропустит и более сложный адрес:

text = "some-one.or.me@example.company.com";

Рассмотренный шаблон можно упростить (для парсера и, возможно, усложнить для человека), если заметить, что группа ([-.]\w+) используется дважды. Второе вхождение этой группы можно заменить ссылкой на первое ее появление. Это допустимо в рамках алгоритмов, использующих модель NFA.

Так как ссылка осуществляется по номеру, то важно правильно вычислить номер группы шаблона, на которую дается ссылка. Нулевая группа — это все выражение, первая группа — это содержимое внутри первой пары круглых скобок, вторая — второй, и т. д. Применяя эти рассуждения, получим другую форму шаблона:

pattern = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+\2*";

Здесь \2 — это ссылка на вторую группу шаблона, то есть на ([-.]\w+). Итак, пытаемся прочесть все выражение. Адрес email должен удовлетворять строке, которая:

Подстрока

Смысл

\w+

Начинается с одного или более элементов множества w (буквы плюс подчеркивание)

[-+.]

Может содержать один (или более) дефис или точку

\w+

За которым следует один или более элементов множества w

([-+.]\w+)

Сформирована группа номер один

([-+.]\w+)*

Может содержать группу номер один

@

Должна содержать символ @

\w+([-.]\w+)*

Этот фрагмент расшифрован выше. Здесь появилась группа номер 2:    ([-.]\w+)

\.

Должна содержать точку

\w+

Содержит один или более элементов множества w

\2*

Может содержать группу номер 2

Используя таблицу операторов, вы легко вычислите, что строка (она приведена в MSDN):

pattern = @"([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)";

также может быть частью шаблона email. Она соответствует одному из вариантов записи адреса, когда почтовый сервер задан в цифровом формате TCP/IP адреса. Шаблон требует троекратного (или более) вхождения последовательностей вида "ddd.", где d — это цифра. Заметим, что его можно упростить:

pattern = @"(\d{1,3}\.){3,}";

Существует и другой способ записи этого шаблона: @"(\d{1,3}\.)\1\1", в котором дважды (\1\1) используется ссылка на группу (\d{1,3}\.). Номер 1 автоматически присвоен группе парсером.

Задержимся немного на определении диапазонов. Шаблон [0-35-9] фактически задает два поддиапазона [0-3] и [5-9]. Ему соответствуют все цифры, кроме цифры 4. Возникает желание оптимизировать выражение и записать его в виде [^4]. Однако, этот шаблон не эквивалентен первому, так как ему удовлетворяют не только цифры из [0-3] и [5-9], но и все другие символы.

Коллекции совпадений, групп и захватов

Рассмотрим фрагмент кода, который демонстрирует, как работать с коллекциями объектов классов, задействованных в технологии регулярных выражений. Но сначала в шаблоне регулярного выражения обойдемся без групп (не считая нулевой, которая всегда присутствует).

text = "bitbitbit";

pattern = "bit"; // Ищем все вхождения "bit"

Regex ex = new Regex (pattern);

MatchCollection mc = ex.Matches(text);     // Коллекция всех совпадений

Console.WriteLine (mc.Count > 0 ? ("There are "+mc.Count+" matches\n") : "No matches");

int nMatch = 0;

foreach (Match m in mc)   // Проход по всей коллекции совпадений

{

Console.WriteLine (string.Format("{0}. Match: '{1}',  Pos = {2}, {3} Groups",

++nMatch, m, m.Index, m.Groups.Count));

int nGroup = 0;

foreach (Group g in m.Groups) // Проход по всей коллекции групп

{

Console.WriteLine (string.Format(

"\t{0}. Group: '{1}', {2} Captures", nGroup++, g, g.Captures.Count));

foreach (Capture c in g.Captures)     // Проход по всей коллекции захватов

Console.WriteLine (string.Format("\t\tCapture: '{0}', Pos = {1}", c, c.Index));

}

}

Этот фрагмент производит следующий, вполне очевидный вывод.

There are 3 matches

1. Match: 'bit',  Pos = 0, 1 Groups

0. Group: 'bit', 1 Captures

Capture: 'bit', Pos = 0

2. Match: 'bit',  Pos = 3, 1 Groups

0. Group: 'bit', 1 Captures