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

Альтернативную конструкцию такого рода удобно вставить внутрь еще одной группы (?:  ), конструкция которой вам не знакома. Группы такого вида называются noncapturing groups, то есть группы, не способные генерировать частичные совпадения (Captures). Они нужны для объединения выражений, результатом которых является группа. В нашем случае группа с именем 1 выбирается на этапе выполнения как альтернатива из двух возможных вариантов. Другие, не рассмотренные нами конструкции групп, вы можете увидеть в MSDN (см. раздел .NET Developement4.NET Framework SDK4.NET Framework4Regular Expressions Language Elements4Grouping Constructs).

После этого осталось дополнить регулярное выражение теми обязательными элементами (href =), по которым, собственно, и ведется поиск ссылок. Окончательно регулярное выражение будет иметь такой вид:

pattern = "href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>[^\\s>]+))";

Теперь, когда выполнена самая трудная часть работы, осталось написать код, который:

¨  Прочтет HTML-документ (файл "Graphics Formats.htm") и поместит его содержимое в испытуемую строку text,

¨  Вызовет метод DumpHrefs, который следует создать и реализовать в нем ту идею выборки групп, которая пояснена выше,

Начнем с разработки метода DumpHrefs. В нем мы задействуем другой конструктор класса Regex, который позволяет игнорировать регистр символов испытуемой строки. Нам это необходимо, так как язык HTML не проявляет строгость к регистру и атрибут href может иметь произвольный вид, HREF, Href и т. д. Кроме этой настройки мы включим бит, который заставит откомпилировать регулярное выражение перед тем как использовать его в цикле поиска совпадений. Это ускорит работу функции DumpHrefs. Затем скорректируем код метода Test, который выполнит первую часть работы.

using System;

using System.Text.RegularExpressions;

using System.IO;

namespace MyRegex

{

class TestRegex

{

private string text, pattern;

private void Test()

{

pattern = "href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>[^\\s>]+))";

Regex ex = new Regex (pattern);

StreamReader sr = new StreamReader ("C:/Graphics Formats.htm");

string line;

while ((line = sr.ReadLine()) != null)

...Ваш код (одна строка)

//Console.Write (text);

DumpHrefs ();

}

void DumpHrefs ()

{

Regex r = new Regex (pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);

Console.WriteLine ("\nThe List of references\n");

for (Match m = r.Match(text);  m.Success;  m = m.NextMatch())

Console.WriteLine ("Position: " + m.Groups[1].Index + ", Value: " + m.Groups[1]);

}

[STAThread]

static void Main() { (new TestRegex()).Test(); }

}

}

Здесь вы видите цикл, который медленно (осторожно) продвигается по тексту, за каждый проход отыскивая одно совпадение. Он приведен только для того, чтобы показать такую возможность. Вы можете заменить его на более скоростной вариант:

void DumpHrefs ()

{

Regex r = new Regex (pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);

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

Console.WriteLine (mc.Count > 0 ? ("\nThe List of references\n") : "No references");

int n = 0;

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

Console.Write("\n{0}. Pos = {1}, Reference: {2}", ++n, m.Groups[1].Index,m.Groups[1]);

}

Приложение производит следующий вывод (приведена только его часть). Позиции ссылок в данном контексте не имеют практической ценности и выведены лишь в учебных целях. Сравнивая этот вывод с исходным HTML-кодом, можно убедиться, что работают обе ветви шаблона.

The List of references

1. Pos = 364, Reference: http://www.mcwi.com/dxf13/dxf_01.html

2. Pos = 454, Reference: 3d/DXF12.spec

3. Pos = 520, Reference: http://www.mcwi.com/dxf11-12/r12dxf_01.html

4. Pos = 588, Reference: 3d/DXF10.spec

5. Pos = 654, Reference: http://www.mcwi.com/dxf10/r10dxf_01.html

6. Pos = 719, Reference: 3d/DXF.info

7. Pos = 796, Reference: 3d/3DS.spec

8. Pos = 864, Reference: 3d/MLI.spec

9. Pos = 948, Reference: 3d/BYU.spec