Стандартный класс String позволяет выполнять над строками различные операции, в том числе поиск, замену, вставку и удаление подстрок. Существуют специальные операции, такие как Join, Split, которые облегчают разбор строки на элементы. Тем не менее, есть классы задач по обработке символьной информации, где стандартных возможностей явно не хватает. Чтобы облегчить решение подобных задач, в Net Framework встроен более мощный аппарат работы со строками, основанный на
Пусть T = {a1, a2, ....an} - алфавит символов. Словом в алфавите T называется последовательность записанных подряд символов, а длиной слова - число его символов. Пустое слово, не содержащее символов, обычно обозначается как e. Алфавит T можно рассматривать как множество всех слов длины 1. Рассмотрим операцию конкатенации над множествами, так, что конкатенация алфавита T с самим собой дает множество всех слов длины 2. Обозначается конкатенация ТТ как Т2. Множество всех слов длины k обозначается - Tk, его можно рассматривать как k -кратную конкатенацию алфавита T. Множество всех непустых слов произвольной длины, полученное объединением всех множеств Tk, обозначается T+, а объединение этого множества с пустым словом называется T*. L(T), содержащееся в T*, называется языком в алфавите T.
Определим класс языков, задаваемых
P и Q являются регулярными, то множества, построенные применением операций объединения, конкатенации и P>>Q, PQ, P*, Q* - тоже являются регулярными.f задает одноэлементное множество {f} при условии, что f - символ алфавита T ;p и q - p+q, pq, p*, q* - являются По сути, L(T) в алфавите T. Этот класс языков - достаточно мощный, с его помощью можно описать интересные языки, но устроены они довольно просто - их можно определить также с помощью простых грамматик, например, правосторонних грамматик. Более важно, что для любого
С точки зрения практики
@ -константа. Чаще всего, следует использовать именно @ -константу. Дело в том, что символ " \ " широко применяется в @ -константы не выдают ошибок и корректно интерпретируют запись
Синтаксис
Конечно, abc " задает образец поиска, так что при вызове соответствующего метода будут разыскиваться одно или все вхождения подстроки " abc " в искомую строку. Но могут существовать и очень сложно устроенные
Повторяю, данная таблица не полна. В ней не отражены, например, такие категории, как подстановки,
Для приведенных категорий также не дан полный список возможных символов.
| Символ | Интерпретация |
|---|---|
| Категория: escape-последовательности | |
\b |
При использовании его в квадратных скобках соответствует символу "обратная косая черта" с кодом - \u0008 |
\t |
Соответствует символу табуляции \u0009 |
\r |
Соответствует символу возврата каретки \u000D |
\n |
Соответствует символу новой строки \u000A |
\e |
Соответствует символу escape \u001B |
\040 |
Соответствует символу ASCII, заданному кодом до трех цифр в восьмеричной системе |
\x20 |
Соответствует символу ASCII, заданному кодом из двух цифр в шестнадцатеричной системе |
\u0020 |
Соответствует символу Unicode, заданному кодом из четырех цифр в шестнадцатеричной системе |
| Категория: подмножества ( | |
| . | Соответствует любому символу, за исключением символа конца строки |
[aeiou] |
Соответствует любому символу из множества, заданного в квадратных скобках |
[^aeiou] |
Отрицание. Соответствует любому символу, за исключением символов, заданных в квадратных скобках |
[0-9a-fA-F] |
Задание |
\p{name} |
Соответствует любому символу, заданному множеству с именем name, например, имя Ll задает множество букв латиницы в нижнем регистре. Поскольку все символы разбиты на подмножества, задаваемые категорией Unicode, то в качестве имени можно задавать имя категории |
\P{name} |
Отрицание. Большая буква всегда задает отрицание множества, заданного малой буквой |
\w |
Множество символов, используемых при задании идентификаторов - большие и малые символы латиницы, цифры и знак подчеркивания |
\s |
Соответствует символам белого пробела |
\d |
Соответствует любому символу из множества цифр |
| Категория: Операции (модификаторы) | |
* |
\w* |
(abc)*. |
Аналогично, {0,} |
+ |
Положительная \w+ или (abc)+. Аналогично, {1,} |
| ? | Задает ноль или одно соответствие; например, \w? или (abc)?. Аналогично, {0,1} |
{n} |
Задает в точности n соответствий; например, \w{2} |
{n,} |
Задает, по меньшей мере, n соответствий; например, (abc){2,} |
{n,m} |
Задает, по меньшей мере, n, но не более m соответствий; например, (abc){2,5} |
| Категория: Группирование | |
(?<Name>) |
При обнаружении соответствия выражению, заданному в круглых скобках, создается именованная Name. Например, (?<tel> \d{7}). При обнаружении последовательности из семи цифр будет создана tel |
() |
Круглые скобки разбивают |
(?imnsx) |
Включает или выключает в (?i-s: ) включает опцию i, задающую нечувствительность к регистру, и выключает опцию s - статус single-line |
В данном пространстве расположено семейство из одного перечисления и восьми связанных между собой классов.
Это основной класс, всегда создаваемый при работе с RegexOptions и задающий опции, которые действуют при работе с данным объектом. Среди опций отмечу одну: ту, что позволяет компилировать
Рассмотрим четыре основных метода
Метод Match запускает поиск соответствия. В качестве параметра методу передается строка поиска, где разыскивается первая подстрока, которая удовлетворяет образцу, заданному
Метод Matches позволяет разыскать все вхождения, то есть все подстроки, удовлетворяющие образцу. У алгоритма поиска есть важная особенность - разыскиваются непересекающиеся вхождения подстрок. Можно считать, что метод Matches многократно запускает
Метод NextMatch запускает новый поиск, начиная с того места, на котором остановился предыдущий поиск.
Метод Split является обобщением метода Split класса String. Он позволяет, используя образец, разделить искомую строку на элементы. Поскольку образец может быть устроен сложнее, чем простое множество разделителей, то метод Split String.
Как уже говорилось, объекты этих классов создаются автоматически при вызове Matches. foreach для последовательного доступа ко всем элементам коллекции.
Capture. При работе с объектами
Index, Length и Value наследованы от прародителя Capture. Они описывают найденную подстроку- индекс начала подстроки в искомой строке, длину подстроки и ее значение;Groups GroupCollection, который позволяет работать с Captures, наследованное от объекта Group, возвращает коллекцию CaptureCollection. Как видите, при работе с Коллекция GroupCollection возвращается при вызове свойства Group объекта Match. Имея эту коллекцию, можно добраться до каждого объекта Group, в нее входящего. Capture и, одновременно, родителем Index, Length и Value, которые и передает своему потомку.
Давайте рассмотрим чуть более подробно, когда и как создаются
В заключение отмечу, что создание именованных
Коллекция CaptureCollection возвращается при вызове свойства Captures объектов Capture, входящий в коллекцию, характеризует соответствие, захваченное в процессе поиска, - соответствующую подстроку. Но поскольку свойства объекта Capture передаются по наследству его потомкам, то можно избежать непосредственной работы с объектами Capture. По крайней мере, в моих примерах не встретится работа с этим объектом, хотя "за кулисами" он непременно присутствует.
Объекты этого перечисления описывают опции, влияющие на то, как устанавливается соответствие. Обычно такой объект создается первым и передается конструктору объекта Compiled, влияющей на эффективность работы
При работе со сложными и большими текстами полезно предварительно скомпилировать используемые в процессе поиска RegexCompilationInfo и передать ему информацию о Compiled. К сожалению, соответствующих примеров на эту тему не будет.
Полагаю, что примеры дополнят краткое описание возможностей FindMatch, которая производит поиск первого вхождения подстроки, соответствующей образцу:
static string FindMatch(string str, string strpat)
{
Regex pat = new Regex(strpat);
Match match =pat.Match(str);
string found = "";
if (match.Success)
{
found =match.Value;
Console.WriteLine("Строка ={0}\tОбразец={1}\
tНайдено={2}", str,strpat,found);
}
return(found);
}//FindMatch
В качестве входных аргументов функции передается строка str, в которой ищется вхождение, и строка strpat, задающая образец - pat match using System.Text.RegularExpressions.)
Поскольку запись
public void TestSinglePat()
{
//поиск по образцу первого вхождения
string str,strpat,found;
Console.WriteLine("Поиск по образцу");
//образец задает подстроку, начинающуюся с символа a,
//далее идут буквы или цифры.
str ="start"; strpat =@"a\w+";
found = FindMatch(str,strpat);
str ="fab77cd efg";
found = FindMatch(str,strpat);
//образец задает подстроку,начинающуюся с символа a,
//заканчивающуюся f с возможными символами b и d в середине
strpat = "a(b|d)*f"; str = "fabadddbdf";
found = FindMatch(str,strpat);
//диапазоны и escape-символы
strpat = "[X-Z]+"; str = "aXYb";
found = FindMatch(str,strpat);
strpat = @"\u0058Y\x5A"; str = "aXYZb";
found = FindMatch(str,strpat);
}//TestSinglePat
Некоторые комментарии к этой процедуре.
@ -константами, описанными в лекции 14. Здесь они как нельзя кстати.
В первом образце используется последовательность символов \w+, обозначающая, как следует из таблицы 15.1, непустую последовательность латиницы и цифр. В совокупности образец задает подстроку, начинающуюся символом a, за которым следуют буквы или цифры (хотя бы одна). Этот образец применяется к двум различным строкам.
В следующем образце используется символ * для обозначения f, между которыми находится возможно пустая последовательность символов из b и d.
Последующие два образца демонстрируют использование диапазонов и escape-последовательностей для представления символов, заданных кодами (в Unicode и шестнадцатеричной кодировке).
Взгляните на результаты, полученные при работе этой процедуры.
(рис 15.1) Регулярные выражения. Поиск по образцу Не всякий класс языков можно описать с помощью L1 в алфавите T={0,1}, которому принадлежат пустое слово и слова, содержащие четное число нулей и четное число единиц. В качестве другого примера рассмотрим язык L2, отличающийся от первого тем, что в нем число единиц нечетно. Оба языка можно задать
(00|11)*((01|10)(00|11)*(01|10)(00|11)*)*
Дадим содержательное описание этого языка. Слова языка представляют возможно пустую последовательность из пар одинаковых символов. Далее может идти последовательность, начинающаяся и заканчивающаяся парами различающихся символов, между которыми может стоять произвольное число пар одинаковых символов. Такая L1.
Язык L2 описать теперь совсем просто. Его слова представляют собой единицу, окаймленную словами языка L1.
Прежде чем перейти к примеру распознавания слов языков L1 и L2, приведу процедуру FindMatches, позволяющую найти все вхождения образца в заданный текст:
void FindMatches(string str, string strpat)
{
Regex pat = new Regex(strpat);
MatchCollection matchcol =pat.Matches(str);
Console.WriteLine("Строка ={0}\tОбразец={1}",str,strpat);
Console.WriteLine("Число совпадений ={0}",matchcol.Count);
foreach(Match match in matchcol)
Console.WriteLine("Index = {0} Value = {1}, Length ={2}",
match.Index,match.Value, match.Length);
}//FindMatches
Входные аргументы у процедуры те же, что и у функции FindMatch, ищущей первое вхождение. Я не стал задавать FindMatch, начинается с создания объекта pat String, относится к неизменяемым (pat.
В отличие от FindMatch, объект pat вызывает метод Matches, который определяет все вхождения подстрок, удовлетворяющих образцу, в заданный текст. Результатом выполнения метода Matches является автоматически создаваемый объект класса
Вот процедура, в которой многократно вызывается FindMatches для различных строк и образцов поиска:
public void TestMultiPat()
{
//поиск по образцу всех вхождений
string str,strpat,found;
Console.WriteLine("Распознавание языков: чет и нечет");
//четное число нулей и единиц
strpat ="((00|11)*((01|10)(00|11)*(01|10)(00|11)*)*)";
str = "0110111101101";
FindMatches(str, strpat);
//четное число нулей и нечетное единиц
string strodd = strpat + "1" + strpat;
FindMatches(str, strodd);
}//TestMultiPat
Коротко прокомментирую работу этой процедуры. Первые два примера связаны с распознаванием языков L1 и L2 (чет и нечет) - языков с четным числом единиц и нулей в первом случае и нечетным числом единиц во втором. L1 метод находит три соответствия. Первое из них задает максимально длинную подстроку, содержащую четное число нулей и единиц, и две пустые подстроки, по определению принадлежащие языку L1. Для языка L2 находится одно соответствие - это сама входная строка. Взгляните на результаты распознавания.
(рис 15.2) Регулярные выражения. Пример "чет и нечет"
Следующий образец в нашем примере позволяет прояснить некоторые особенности работы метода Matches. Сколько раз строка "око" входит в строку "рококо" - один или два? Все зависит от того, как считать. С точки зрения метода Matches, - один раз, поскольку он разыскивает непересекающиеся вхождения, начиная очередной поиск вхождения подстроки с того места, где закончилось предыдущее вхождение. Еще один пример на эту же тему работает с числовыми строками.
Console.WriteLine("око и рококо");
strpat="око"; str = "рококо";
FindMatches(str, strpat);
strpat="123";
str= "0123451236123781239";
FindMatches(str, strpat);
На рис. 15.3 показаны результаты поисков.
(рис 15.3) Регулярные выражения. Пример "око и рококо"
Этот пример на поиск множественных соответствий навеян словами песни Высоцкого, где говорится, что дикари не смогли распознать, где кок, а где Кук. Наше
Console.WriteLine("кок и кук");
strpat="(т|к).(т|к)";
str="кок тот кук тут как кот";
FindMatches(str, strpat);
Вот результаты работы этого фрагмента кода.
(рис 15.4) Регулярные выражения. Пример "кок и кук"
В этом примере рассматривается ранее упоминавшаяся, но не описанная возможность задания в \k ", после которой идет имя
Console.WriteLine("Ссылка назад - второе вхождение слова");
strpat = @"\s(?<word>\w+)\s\k'word'";
str = "I know know that, You know that!";
FindMatches(str, strpat);
Рассмотрим более подробно strpat. В word ", взятое в угловые скобки. После имени \w+ ". В дальнейшем описании шаблона задается ссылка на word ". Здесь имя
(рис 15.5) Регулярные выражения. Пример "обратные ссылки"
Давайте вернемся к задаче разбора предложения на элементы. В классе string для этого имеется метод Split, который и решает поставленную задачу. Однако у этого метода есть существенный недостаток, - он не справляется с идущими подряд разделителями и создает для таких пар пустые слова. Метод Split
public void TestParsing()
{
string str,strpat;
//разбор предложения - создание массива слов
str = "А это пшеница, которая в темном чулане
хранится," +" в доме, который построил Джек!";
strpat =" +|, +";
Regex pat = new Regex(strpat);
string[] words;
words = pat.Split(str);
int i=1;
foreach(string word in words)
Console.WriteLine("{0}: {1}",i++,word);
}//TestParsing
strpat, определяет множество разделителей. Заметьте, в качестве разделителя задан пробел, повторенный сколь угодно много раз, либо пара символов - запятая и пробел. Разделители задаются Split применяется к объекту pat Split.
(рис 15.6) Регулярные выражения. Пример "Дом Джека"
Как уже говорилось,
public void TestAttributes()
{
string s1 = "tel: (831-2) 94-20-55 ";
string s2 = "Адрес: 117926, Москва, 5-й Донской проезд,
стр.10,кв.7 ";
string s3 = "e-mail: Valentin.Berestov@tverorg.ru ";
string s4 = s1+ s2 + s3;
string s5 = s2 + s1 + s3;
string pat1 = @"tel:\s(?<tel>\((\d|-)*\)\s(\d|-)+)\s";
string pat2= @"Адрес:\s(?<addr>[0-9А-Яа-я \-\,\.]+)\s";
string pat3 =@"e-mail:\s(?<em>[a-zA-Z\.@]+)\s";
string compat = pat1+pat2+pat3;
string tel="", addr = "", em = "";
Строки s4 и s5 представляют строку разбираемого документа. Их две, для того чтобы можно было проводить эксперименты, когда атрибуты в документе представлены в произвольном порядке. Каждая из строк pat1, pat2, pat3 задает одну именованную tel, Адрес, e-mail - даются в соответствии со смыслом атрибутов. Сами шаблоны подробно описывать не буду - сделаю лишь одно замечание. Например, шаблон телефона исходит из того, что номеру предшествует код, заключенный в круглые скобки. Поскольку сами скобки играют особую роль, то для задания скобки как символа используется пара - " \( ". Это же касается и многих других символов, используемых в шаблонах, - точки, дефиса и т.п. Строка compat представляет составное tel, addr и em нам понадобятся для размещения в них результатов разбора. Применим вначале к строкам s4 и s5 каждый из шаблонов pat1, pat2, pat3 в отдельности и выделим соответствующий атрибут из строки. Вот код, выполняющий эти операции:
Regex reg1 = new Regex(pat1);
Match match1= reg1.Match(s4);
Console.WriteLine("Value =" + match1.Value);
tel= match1.Groups["tel"].Value;
Console.WriteLine(tel);
Regex reg2 = new Regex(pat2);
Match match2= reg2.Match(s5);
Console.WriteLine("Value =" + match2.Value);
addr= match2.Groups["addr"].Value;
Console.WriteLine(addr);
Regex reg3 = new Regex(pat3);
Match match3= reg3.Match(s5);
Console.WriteLine("Value =" + match3.Value);
em= match3.Groups["em"].Value;
Console.WriteLine(em);
Все выполняется нужным образом - создаются именованные compat:
Regex comreg = new Regex(compat); Match commatch= comreg.Match(s4); tel= commatch.Groups["tel"].Value; Console.WriteLine(tel); addr= commatch.Groups["addr"].Value; Console.WriteLine(addr); em= commatch.Groups["em"].Value; Console.WriteLine(em); }// TestAttributes
И эта задача успешно решается. Взгляните на результаты разбора текста.
(рис 15.7) Регулярные выражения. Пример "Атрибуты"На этом и завершим рассмотрение
Стандартный класс String позволяет выполнять над строками различные операции, в том числе поиск, замену, вставку и удаление подстрок. Существуют специальные операции, такие как Join, Split, которые облегчают разбор строки на элементы. Тем не менее, есть классы задач по обработке символьной информации, где стандартных возможностей явно не хватает. Чтобы облегчить решение подобных задач, в Net Framework встроен более мощный аппарат работы со строками, основанный на
Пусть T = {a1, a2, ....an} - алфавит символов. Словом в алфавите T называется последовательность записанных подряд символов, а длиной слова - число его символов. Пустое слово, не содержащее символов, обычно обозначается как e. Алфавит T можно рассматривать как множество всех слов длины 1. Рассмотрим операцию конкатенации над множествами, так, что конкатенация алфавита T с самим собой дает множество всех слов длины 2. Обозначается конкатенация ТТ как Т2. Множество всех слов длины k обозначается - Tk, его можно рассматривать как k -кратную конкатенацию алфавита T. Множество всех непустых слов произвольной длины, полученное объединением всех множеств Tk, обозначается T+, а объединение этого множества с пустым словом называется T*. L(T), содержащееся в T*, называется языком в алфавите T.
Определим класс языков, задаваемых
P и Q являются регулярными, то множества, построенные применением операций объединения, конкатенации и P>>Q, PQ, P*, Q* - тоже являются регулярными.f задает одноэлементное множество {f} при условии, что f - символ алфавита T ;p и q - p+q, pq, p*, q* - являются По сути, L(T) в алфавите T. Этот класс языков - достаточно мощный, с его помощью можно описать интересные языки, но устроены они довольно просто - их можно определить также с помощью простых грамматик, например, правосторонних грамматик. Более важно, что для любого
С точки зрения практики
@ -константа. Чаще всего, следует использовать именно @ -константу. Дело в том, что символ " \ " широко применяется в @ -константы не выдают ошибок и корректно интерпретируют запись
Синтаксис
Конечно, abc " задает образец поиска, так что при вызове соответствующего метода будут разыскиваться одно или все вхождения подстроки " abc " в искомую строку. Но могут существовать и очень сложно устроенные
Повторяю, данная таблица не полна. В ней не отражены, например, такие категории, как подстановки,
Для приведенных категорий также не дан полный список возможных символов.
| Символ | Интерпретация |
|---|---|
| Категория: escape-последовательности | |
\b |
При использовании его в квадратных скобках соответствует символу "обратная косая черта" с кодом - \u0008 |
\t |
Соответствует символу табуляции \u0009 |
\r |
Соответствует символу возврата каретки \u000D |
\n |
Соответствует символу новой строки \u000A |
\e |
Соответствует символу escape \u001B |
\040 |
Соответствует символу ASCII, заданному кодом до трех цифр в восьмеричной системе |
\x20 |
Соответствует символу ASCII, заданному кодом из двух цифр в шестнадцатеричной системе |
\u0020 |
Соответствует символу Unicode, заданному кодом из четырех цифр в шестнадцатеричной системе |
| Категория: подмножества ( | |
| . | Соответствует любому символу, за исключением символа конца строки |
[aeiou] |
Соответствует любому символу из множества, заданного в квадратных скобках |
[^aeiou] |
Отрицание. Соответствует любому символу, за исключением символов, заданных в квадратных скобках |
[0-9a-fA-F] |
Задание |
\p{name} |
Соответствует любому символу, заданному множеству с именем name, например, имя Ll задает множество букв латиницы в нижнем регистре. Поскольку все символы разбиты на подмножества, задаваемые категорией Unicode, то в качестве имени можно задавать имя категории |
\P{name} |
Отрицание. Большая буква всегда задает отрицание множества, заданного малой буквой |
\w |
Множество символов, используемых при задании идентификаторов - большие и малые символы латиницы, цифры и знак подчеркивания |
\s |
Соответствует символам белого пробела |
\d |
Соответствует любому символу из множества цифр |
| Категория: Операции (модификаторы) | |
* |
\w* |
(abc)*. |
Аналогично, {0,} |
+ |
Положительная \w+ или (abc)+. Аналогично, {1,} |
| ? | Задает ноль или одно соответствие; например, \w? или (abc)?. Аналогично, {0,1} |
{n} |
Задает в точности n соответствий; например, \w{2} |
{n,} |
Задает, по меньшей мере, n соответствий; например, (abc){2,} |
{n,m} |
Задает, по меньшей мере, n, но не более m соответствий; например, (abc){2,5} |
| Категория: Группирование | |
(?<Name>) |
При обнаружении соответствия выражению, заданному в круглых скобках, создается именованная Name. Например, (?<tel> \d{7}). При обнаружении последовательности из семи цифр будет создана tel |
() |
Круглые скобки разбивают |
(?imnsx) |
Включает или выключает в (?i-s: ) включает опцию i, задающую нечувствительность к регистру, и выключает опцию s - статус single-line |
В данном пространстве расположено семейство из одного перечисления и восьми связанных между собой классов.
Это основной класс, всегда создаваемый при работе с RegexOptions и задающий опции, которые действуют при работе с данным объектом. Среди опций отмечу одну: ту, что позволяет компилировать
Рассмотрим четыре основных метода
Метод Match запускает поиск соответствия. В качестве параметра методу передается строка поиска, где разыскивается первая подстрока, которая удовлетворяет образцу, заданному
Метод Matches позволяет разыскать все вхождения, то есть все подстроки, удовлетворяющие образцу. У алгоритма поиска есть важная особенность - разыскиваются непересекающиеся вхождения подстрок. Можно считать, что метод Matches многократно запускает
Метод NextMatch запускает новый поиск, начиная с того места, на котором остановился предыдущий поиск.
Метод Split является обобщением метода Split класса String. Он позволяет, используя образец, разделить искомую строку на элементы. Поскольку образец может быть устроен сложнее, чем простое множество разделителей, то метод Split String.
Как уже говорилось, объекты этих классов создаются автоматически при вызове Matches. foreach для последовательного доступа ко всем элементам коллекции.
Capture. При работе с объектами
Index, Length и Value наследованы от прародителя Capture. Они описывают найденную подстроку- индекс начала подстроки в искомой строке, длину подстроки и ее значение;Groups GroupCollection, который позволяет работать с Captures, наследованное от объекта Group, возвращает коллекцию CaptureCollection. Как видите, при работе с Коллекция GroupCollection возвращается при вызове свойства Group объекта Match. Имея эту коллекцию, можно добраться до каждого объекта Group, в нее входящего. Capture и, одновременно, родителем Index, Length и Value, которые и передает своему потомку.
Давайте рассмотрим чуть более подробно, когда и как создаются
В заключение отмечу, что создание именованных
Коллекция CaptureCollection возвращается при вызове свойства Captures объектов Capture, входящий в коллекцию, характеризует соответствие, захваченное в процессе поиска, - соответствующую подстроку. Но поскольку свойства объекта Capture передаются по наследству его потомкам, то можно избежать непосредственной работы с объектами Capture. По крайней мере, в моих примерах не встретится работа с этим объектом, хотя "за кулисами" он непременно присутствует.
Объекты этого перечисления описывают опции, влияющие на то, как устанавливается соответствие. Обычно такой объект создается первым и передается конструктору объекта Compiled, влияющей на эффективность работы
При работе со сложными и большими текстами полезно предварительно скомпилировать используемые в процессе поиска RegexCompilationInfo и передать ему информацию о Compiled. К сожалению, соответствующих примеров на эту тему не будет.
Полагаю, что примеры дополнят краткое описание возможностей FindMatch, которая производит поиск первого вхождения подстроки, соответствующей образцу:
static string FindMatch(string str, string strpat)
{
Regex pat = new Regex(strpat);
Match match =pat.Match(str);
string found = "";
if (match.Success)
{
found =match.Value;
Console.WriteLine("Строка ={0}\tОбразец={1}\
tНайдено={2}", str,strpat,found);
}
return(found);
}//FindMatch
В качестве входных аргументов функции передается строка str, в которой ищется вхождение, и строка strpat, задающая образец - pat match using System.Text.RegularExpressions.)
Поскольку запись
public void TestSinglePat()
{
//поиск по образцу первого вхождения
string str,strpat,found;
Console.WriteLine("Поиск по образцу");
//образец задает подстроку, начинающуюся с символа a,
//далее идут буквы или цифры.
str ="start"; strpat =@"a\w+";
found = FindMatch(str,strpat);
str ="fab77cd efg";
found = FindMatch(str,strpat);
//образец задает подстроку,начинающуюся с символа a,
//заканчивающуюся f с возможными символами b и d в середине
strpat = "a(b|d)*f"; str = "fabadddbdf";
found = FindMatch(str,strpat);
//диапазоны и escape-символы
strpat = "[X-Z]+"; str = "aXYb";
found = FindMatch(str,strpat);
strpat = @"\u0058Y\x5A"; str = "aXYZb";
found = FindMatch(str,strpat);
}//TestSinglePat
Некоторые комментарии к этой процедуре.
@ -константами, описанными в лекции 14. Здесь они как нельзя кстати.
В первом образце используется последовательность символов \w+, обозначающая, как следует из таблицы 15.1, непустую последовательность латиницы и цифр. В совокупности образец задает подстроку, начинающуюся символом a, за которым следуют буквы или цифры (хотя бы одна). Этот образец применяется к двум различным строкам.
В следующем образце используется символ * для обозначения f, между которыми находится возможно пустая последовательность символов из b и d.
Последующие два образца демонстрируют использование диапазонов и escape-последовательностей для представления символов, заданных кодами (в Unicode и шестнадцатеричной кодировке).
Взгляните на результаты, полученные при работе этой процедуры.
(рис 15.1) Регулярные выражения. Поиск по образцуНе всякий класс языков можно описать с помощью L1 в алфавите T={0,1}, которому принадлежат пустое слово и слова, содержащие четное число нулей и четное число единиц. В качестве другого примера рассмотрим язык L2, отличающийся от первого тем, что в нем число единиц нечетно. Оба языка можно задать
(00|11)*((01|10)(00|11)*(01|10)(00|11)*)*
Дадим содержательное описание этого языка. Слова языка представляют возможно пустую последовательность из пар одинаковых символов. Далее может идти последовательность, начинающаяся и заканчивающаяся парами различающихся символов, между которыми может стоять произвольное число пар одинаковых символов. Такая L1.
Язык L2 описать теперь совсем просто. Его слова представляют собой единицу, окаймленную словами языка L1.
Прежде чем перейти к примеру распознавания слов языков L1 и L2, приведу процедуру FindMatches, позволяющую найти все вхождения образца в заданный текст:
void FindMatches(string str, string strpat)
{
Regex pat = new Regex(strpat);
MatchCollection matchcol =pat.Matches(str);
Console.WriteLine("Строка ={0}\tОбразец={1}",str,strpat);
Console.WriteLine("Число совпадений ={0}",matchcol.Count);
foreach(Match match in matchcol)
Console.WriteLine("Index = {0} Value = {1}, Length ={2}",
match.Index,match.Value, match.Length);
}//FindMatches
Входные аргументы у процедуры те же, что и у функции FindMatch, ищущей первое вхождение. Я не стал задавать FindMatch, начинается с создания объекта pat String, относится к неизменяемым (pat.
В отличие от FindMatch, объект pat вызывает метод Matches, который определяет все вхождения подстрок, удовлетворяющих образцу, в заданный текст. Результатом выполнения метода Matches является автоматически создаваемый объект класса
Вот процедура, в которой многократно вызывается FindMatches для различных строк и образцов поиска:
public void TestMultiPat()
{
//поиск по образцу всех вхождений
string str,strpat,found;
Console.WriteLine("Распознавание языков: чет и нечет");
//четное число нулей и единиц
strpat ="((00|11)*((01|10)(00|11)*(01|10)(00|11)*)*)";
str = "0110111101101";
FindMatches(str, strpat);
//четное число нулей и нечетное единиц
string strodd = strpat + "1" + strpat;
FindMatches(str, strodd);
}//TestMultiPat
Коротко прокомментирую работу этой процедуры. Первые два примера связаны с распознаванием языков L1 и L2 (чет и нечет) - языков с четным числом единиц и нулей в первом случае и нечетным числом единиц во втором. L1 метод находит три соответствия. Первое из них задает максимально длинную подстроку, содержащую четное число нулей и единиц, и две пустые подстроки, по определению принадлежащие языку L1. Для языка L2 находится одно соответствие - это сама входная строка. Взгляните на результаты распознавания.
(рис 15.2) Регулярные выражения. Пример "чет и нечет"
Следующий образец в нашем примере позволяет прояснить некоторые особенности работы метода Matches. Сколько раз строка "око" входит в строку "рококо" - один или два? Все зависит от того, как считать. С точки зрения метода Matches, - один раз, поскольку он разыскивает непересекающиеся вхождения, начиная очередной поиск вхождения подстроки с того места, где закончилось предыдущее вхождение. Еще один пример на эту же тему работает с числовыми строками.
Console.WriteLine("око и рококо");
strpat="око"; str = "рококо";
FindMatches(str, strpat);
strpat="123";
str= "0123451236123781239";
FindMatches(str, strpat);
На рис. 15.3 показаны результаты поисков.
(рис 15.3) Регулярные выражения. Пример "око и рококо"
Этот пример на поиск множественных соответствий навеян словами песни Высоцкого, где говорится, что дикари не смогли распознать, где кок, а где Кук. Наше
Console.WriteLine("кок и кук");
strpat="(т|к).(т|к)";
str="кок тот кук тут как кот";
FindMatches(str, strpat);
Вот результаты работы этого фрагмента кода.
(рис 15.4) Регулярные выражения. Пример "кок и кук"
В этом примере рассматривается ранее упоминавшаяся, но не описанная возможность задания в \k ", после которой идет имя
Console.WriteLine("Ссылка назад - второе вхождение слова");
strpat = @"\s(?<word>\w+)\s\k'word'";
str = "I know know that, You know that!";
FindMatches(str, strpat);
Рассмотрим более подробно strpat. В word ", взятое в угловые скобки. После имени \w+ ". В дальнейшем описании шаблона задается ссылка на word ". Здесь имя
(рис 15.5) Регулярные выражения. Пример "обратные ссылки"
Давайте вернемся к задаче разбора предложения на элементы. В классе string для этого имеется метод Split, который и решает поставленную задачу. Однако у этого метода есть существенный недостаток, - он не справляется с идущими подряд разделителями и создает для таких пар пустые слова. Метод Split
public void TestParsing()
{
string str,strpat;
//разбор предложения - создание массива слов
str = "А это пшеница, которая в темном чулане
хранится," +" в доме, который построил Джек!";
strpat =" +|, +";
Regex pat = new Regex(strpat);
string[] words;
words = pat.Split(str);
int i=1;
foreach(string word in words)
Console.WriteLine("{0}: {1}",i++,word);
}//TestParsing
strpat, определяет множество разделителей. Заметьте, в качестве разделителя задан пробел, повторенный сколь угодно много раз, либо пара символов - запятая и пробел. Разделители задаются Split применяется к объекту pat Split.
(рис 15.6) Регулярные выражения. Пример "Дом Джека"
Как уже говорилось,
public void TestAttributes()
{
string s1 = "tel: (831-2) 94-20-55 ";
string s2 = "Адрес: 117926, Москва, 5-й Донской проезд,
стр.10,кв.7 ";
string s3 = "e-mail: Valentin.Berestov@tverorg.ru ";
string s4 = s1+ s2 + s3;
string s5 = s2 + s1 + s3;
string pat1 = @"tel:\s(?<tel>\((\d|-)*\)\s(\d|-)+)\s";
string pat2= @"Адрес:\s(?<addr>[0-9А-Яа-я \-\,\.]+)\s";
string pat3 =@"e-mail:\s(?<em>[a-zA-Z\.@]+)\s";
string compat = pat1+pat2+pat3;
string tel="", addr = "", em = "";
Строки s4 и s5 представляют строку разбираемого документа. Их две, для того чтобы можно было проводить эксперименты, когда атрибуты в документе представлены в произвольном порядке. Каждая из строк pat1, pat2, pat3 задает одну именованную tel, Адрес, e-mail - даются в соответствии со смыслом атрибутов. Сами шаблоны подробно описывать не буду - сделаю лишь одно замечание. Например, шаблон телефона исходит из того, что номеру предшествует код, заключенный в круглые скобки. Поскольку сами скобки играют особую роль, то для задания скобки как символа используется пара - " \( ". Это же касается и многих других символов, используемых в шаблонах, - точки, дефиса и т.п. Строка compat представляет составное tel, addr и em нам понадобятся для размещения в них результатов разбора. Применим вначале к строкам s4 и s5 каждый из шаблонов pat1, pat2, pat3 в отдельности и выделим соответствующий атрибут из строки. Вот код, выполняющий эти операции:
Regex reg1 = new Regex(pat1);
Match match1= reg1.Match(s4);
Console.WriteLine("Value =" + match1.Value);
tel= match1.Groups["tel"].Value;
Console.WriteLine(tel);
Regex reg2 = new Regex(pat2);
Match match2= reg2.Match(s5);
Console.WriteLine("Value =" + match2.Value);
addr= match2.Groups["addr"].Value;
Console.WriteLine(addr);
Regex reg3 = new Regex(pat3);
Match match3= reg3.Match(s5);
Console.WriteLine("Value =" + match3.Value);
em= match3.Groups["em"].Value;
Console.WriteLine(em);
Все выполняется нужным образом - создаются именованные compat:
Regex comreg = new Regex(compat); Match commatch= comreg.Match(s4); tel= commatch.Groups["tel"].Value; Console.WriteLine(tel); addr= commatch.Groups["addr"].Value; Console.WriteLine(addr); em= commatch.Groups["em"].Value; Console.WriteLine(em); }// TestAttributes
И эта задача успешно решается. Взгляните на результаты разбора текста.
(рис 15.7) Регулярные выражения. Пример "Атрибуты"На этом и завершим рассмотрение
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.