Зіставлення шаблону за допомогою Visual C# та регулярних виразів

0 Comments

У цій покроковій статті описується створення та використання регулярних виразів для визначення відповідності рядків певним шаблонам.

Вихідна версія продукту: Visual C#
Вихідний номер бази знань: 308252

Підсумки

Регулярні вирази дозволяють легко аналізувати та зіставляти рядки з певним шаблоном. Використовуючи об'єкти, доступні в RegularExpressions просторі імен, можна порівняти рядок із заданим шаблоном, замінити шаблон рядка іншим рядком або отримати лише частини форматованого рядка. У цьому прикладі ми створимо шаблон для перевірки адреси електронної пошти. Ця стаття відноситься до простору System.Text.RegularExpressions імен бібліотеки класів Microsoft платформа .NET Framework.

Вимоги

У цій статті передбачається, що ви знайомі з такими розділами:

Використання регулярних виразів для зіставлення шаблону

  1. Запустіть Visual C#.
  2. Створіть консольну програму Visual C#.
  3. Вкажіть ключове слово using в Text.RegularExpressions просторі імен, щоб ви не були зобов'язані кваліфікувати оголошення в цих просторах імен пізніше в коді. Оператор using повинен використовуватись перед будь-якими іншими оголошеннями:
використовуючи System.Text.RegularExpressions; 
  1. Захопити підрядок перед символом @ і помістити його до групи user.
  2. Зафіксувати підрядок після символу @ і помістити його до групи host.
  3. Переконайтеся, що перша половина рядка не має символу @ .
Regex emailregex = new Regex("(?[^@]+)@(?.+)"); 
String s = "[email protected]"; 
Match m = emailregex.Match(s); 
if (m.Success) < Console.WriteLine ("User: " + m.Groups["user"].Value); Console.WriteLine ("Host:" + m.Groups["host"].Value); >else < Console.WriteLine (s + " is not a valid email address"); > Console.WriteLine (); 
System.Console.WriteLine("Press Enter to Continue. "); System.Console.ReadLine(); 
  • У меню "Проект" виберіть пункт "Властивості" та натисніть кнопку "Налагодження“. У розділі "Параметри запуску" у правій області вкажіть адресу електронної пошти, яку потрібно перевірити. Натисніть клавішу F5 або натисніть кнопку "Пуск " меню налагодження , щоб запустити програму.
  • Запустіть командне вікно і перейдіть до папку bin або debug у папці, в якій знаходиться проект. Потім введіть ім'я файлу, за яким слідує адреса електронної пошти, яку ви хочете перевірити.
  • Знайдіть виконуваний файл для цього проекту і перетягніть його до початкового файл. Запустіть вікно на панелі завдань. Додайте адресу електронної пошти для перевірки та натисніть кнопку "ОК“.

Посилання

Зіставлення шаблону за допомогою Visual C# та регулярних виразів - Istoriya.v.ua

Регулярні вирази (РВ) це, по суті, крихітна мова програмування, вбудована в Python і доступна за допомогою модуля re. Використовуючи його, ви вказуєте правила для багатьох можливих рядків, які ви хочете перевірити; це безліч може містити англійські фрази, або адреси електронної пошти, або команди TeX, або все що завгодно. За допомогою РВ ви можете ставити запитання, такі як «Чи відповідає цей рядок шаблону?», або «Чи збігається шаблон десь із цим рядком?». Ви також можете використовувати регулярні вирази, щоб змінити рядок або розбити його на частини різними способами.

Шаблони регулярних виразів компілюються в серії байт-коду, які потім виконуються відповідним движком написаним на C. Для просунутого використання може бути важливо приділяти увагу тому, як двигун виконуватиме цей регулярний вираз, і писати його так, щоб виходив байт-код, який працює швидше. Оптимізація не розглядається в цьому документі, оскільки вона вимагає від вас хорошого розуміння внутрішніх деталей двигуна.

Мова регулярних виразів відносно мала і обмежена, тому не всі можливі завдання з обробки рядків можна зробити за допомогою регулярних виразів. Також існують завдання, які можна зробити за допомогою регулярних виразів, але вирази виявляються надто складними. У цих випадках може бути краще написати звичайний Python код, нехай він буде працювати повільніше, ніж розроблений регулярний вираз, але буде зрозумілішим.

Прості шаблони

Ми почнемо з вивчення найпростіших регулярних виразів. Оскільки регулярні вирази використовуються для роботи з рядками, ми почнемо з найпоширенішого завдання — відповідність символам.

За детальним поясненням технічної сторони регулярних виразів (детермінованих та недетермінованих кінцевих автоматів) ви можете звернутися до практично будь-якого підручника з написання компіляторів.

Відповідність символів

Більшість літер та символів відповідають самі собі. Наприклад, регулярне вираження test буде точно відповідати рядку test (Ви можете включити режим без урахування регістру, що дозволить цьому регулярному виразу також відповідати Test або TEST, але про це пізніше).

З цього правила є винятки; деякі символи це спеціальні метасимволиі самі собі не відповідають.Натомість вони вказують, що має бути знайдена деяка незвичайна річ, або впливають інші частини регулярного висловлювання, повторюючи чи змінюючи їх значення. Більшість цього посібника присвячена обговоренню різних метасимволів і тому, що вони роблять.

Ось повний перелік метасимволів; їх значення обговорюватимуться в решті цього HOWTO.

Перші метасимволи, що ми розглянемо це [і]. Вони використовуються для визначення класу символів, що є набором символів, з якими ви шукаєте збіг. Символи можуть бути перераховані окремо або у вигляді деякого діапазону символів, позначеного першим і останнім символом, розділених знаком '-' . Наприклад, [abc] буде відповідати будь-якому з символів a, b або c; це те саме, що вираз [a-c] , що використовує діапазон для завдання тієї ж безлічі символів. Якщо ви хочете порівняти лише малі літери, РВ матиме вигляд [a-z] .

Метасимволи не активні усередині класів. Наприклад, [akm$] буде відповідати будь-якому з символів 'a', 'k', 'm' або '$' . Знак $ це зазвичай метасимвол (як видно зі списку символів вище), але всередині класу символів він позбавляється своєї особливої ​​природи.

Для того, щоб знаходити відповідність символам поза цим класом, на початку класу додається символ '^' . Наприклад, вираз [^5] відповідає будь-якому символу, крім '5'.

Мабуть, найбільш важливим є метасимвол зворотної косої межі. Як і в рядкових літералах Python, за бекслешем можуть йти різні символи, що позначають різні спеціальні послідовності.Він також використовується для екранування метасимволів, щоб їх можна було використовувати шаблони; наприклад, якщо потрібно знайти відповідність [ або \ , щоб позбавити їх своєї особливої ​​ролі метасимволів, перед ним потрібно поставити зворотну косу межу: \[ або \ ].

Деякі з спеціальних послідовностей, що починаються з ' представляють зумовлені набори символів, часто бувають корисними, такі як набір цифр, набір літер, або безлічі всього, що не є пробілами, символами табуляції і т.д. буд. (Whitespace). Наступні зумовлені послідовності є їх підмножиною. Повний список послідовностей та розширених визначень класів для Юнікод-рядків дивіться в останній частині Regular Expression Syntax.

\d
Відповідає будь-якій цифрі; еквівалент класу 8.
\D
Відповідає будь-якому нечисловому символу; еквівалент класу [^0-9].
\s
Відповідає будь-якому символу whitespace; еквівалент [\t\n\r\f\v] .
\S
Відповідає будь-якому не-whitespace символу; еквівалент [^ \t\n\r\f\v] .
\w
Відповідає будь-якій букві чи цифрі; еквівалент [a-zA-Z0-9_] .
\W
Навпаки; еквівалент [^a-zA-Z0-9_] .

Ці послідовності можуть бути включені до класу символів. Наприклад, [\s,.] є характер класу, який буде відповідати будь-якому whitespace-символу або коми або точки.

Останній метасимвол у цьому розділі це '.' . Він відповідає всім символам, крім символу нового рядка, але є альтернативний режим (re.DOTALL), де ця множина буде включати і його. '.' часто використовується там, де ви хочете зіставити будь-який символ.

Повторювані речі

Можливість зіставляти різні набори символів це перше, що регулярні вирази можуть зробити і що завжди можна зробити рядковими методами. Однак, якби це було єдиною додатковою можливістю, вони б не були такими цікавими. Інша можливість полягає в тому, що ви можете вказати, скільки разів повинна повторюватися частина регулярного виразу.

Перший метасимвол для повторення це *. Він показує, що попередній символ можна порівняти нуль і більше разів, замість одного порівняння.

Наприклад, ca*t буде відповідати ct (0 символів a), cat (1 символ a), caaat (3 символи a), і так далі. Двигун регулярних виразів має різні внутрішні обмеження, що випливають із розміру int типу для C, що не дозволяє проводити йому зіставлення більше 2 мільярдів символів 'a'. (Сподіваюся, вам це не знадобиться).

Повторення, такі як * називають жадібними (Greedy); двигун намагатиметься повторити його стільки разів, скільки це можливо. Якщо наступні частини шаблону не відповідають, двигун повернеться назад і спробує знову з кількома повторами символу.

Покроковий розгляд якогось прикладу зробить пояснення більш зрозумілим. Давайте розглянемо вираз a[bcd]*b. Воно відповідає букві 'a' , нулю або більше символів класу [bcd] , і нарешті, заключній букві 'b' . Тепер уявімо зіставлення цього регулярного виразу рядку abcbd. Ось як відбувається порівняння поетапно:

1. a – 'a' відповідає регулярному виразу
2. abcbd – движок зіставляє [bcd] * на якомога більшій кількості символів, тобто до кінця рядка (оскільки всі символи відповідають класу в дужках [])
3.Провал – двигун намагається зіставити останній символ у регулярному виразі – букву b, але поточна позиція вже в кінці рядка, де немає жодних символів, так що він зазнає невдачі.
4. abcb – повернулися назад, зменшили на один символ зіставлення з [bcd] *
5. Провал – намагаємося знову знайти b, але в кінці тільки d
6. abc – знову повертаємося назад, тепер [bcd] * це тільки bc
7. abcb – знову шукаємо останній символ регулярного виразу – b. Тепер він дійсно знаходиться на потрібній позиції і ми досягаємо успіху

Отже, було досягнуто кінця РВ і зіставлення з ним дало abcb . Цей приклад показав, як двигун спочатку забирається так далеко, як може, і, якщо не знаходить відповідності, повертається назад, знову і знову працюючи із залишком регулярного вираження. Він буде робити так до тих пір, поки не отримає нуль збігів для [bcd] *, і, якщо і тоді не вийде збігу, то зробить висновок, що рядок зовсім не відповідає шаблону РВ.

Інший метасимвол повторення це + , що повторює послідовність порівняння один або більше разів. Зверніть особливу увагу на різницю між * та +. * вимагає відповідності необхідної частини нуль або більше разів, тобто повторюване може і не бути зовсім, а + вимагає, принаймні одне входження. Для аналогічного прикладу ca+t буде зіставлятися cat або, наприклад, caaat , але ct .

Є ще два специфікатори, що повторюють. Знак питання, ? , Що перевіряє наявність збігу нуль або один раз. Наприклад, home-brew відповідає як homebrew , так і home-brew .

Найбільш повний специфікатор, що повторює це , де m і n – цілі числа. Цей визначник означає, що тут має бути не менше m та не більше n повторень.Наприклад, a/b відповідає a/b , a//b та a///b . Це може бути ab , рядок у якій немає слешей чи a////b , у якій їх чотири.

Ви можете не задавати m або n, тоді для відсутнього передбачається найбільш розумне значення. Опускання m означає, що нижня межа 0, опускання n передбачає верхньою межею нескінченність, але, як говорилося вище, останній обмежений пам'яттю.

Читачі вже могли помітити, що всі три інші специфікатори можуть бути виражені через останній. це те саме, що * , еквівалентно + і може замінювати знак ? .

Використання регулярних виразів

Тепер, коли ми розглянули кілька простих регулярних виразів, як ми можемо використовувати їх у Python? Модуль re надає інтерфейс для регулярних виразів, що дозволяє компілювати регулярні вирази об'єкти, а потім виконувати з ними зіставлення.

Компіляція регулярних виразів

Регулярні вирази компілюються в об'єкти шаблонів, що мають методи різних операцій, таких як пошук входження шаблону або виконання заміни рядка.

re.compile() також приймає необов'язкові аргументи, що використовуються для включення різних особливостей та варіацій синтаксису:

>>> p = re.compile('ab*', re.IGNORECASE)

Регулярний вираз передається re.compile() як рядок. Регулярні вирази обробляються як рядки, оскільки не є частиною мови Python, і немає жодного спеціального синтаксису для їх вираження. (Існують програми, які зовсім не потребують регулярних виразів, так що немає необхідності забивати специфікацію мови, включаючи їх.) Натомість є модуль re , що представляє собою обгортку модуля на С, подібно до модулів socket або zlib .

Передача регулярних виразів у вигляді рядка дозволяє Python бути простішим, але має один недолік, який є темою наступного розділу.

Бекслеш лихо
(Або зворотна коса чума:))

Як було зазначено раніше, у регулярних виразах для того, щоб позначити спеціальну форму або дозволити символам втратити їхню особливу роль, використовується символ бекслешу ( '\'). Це призводить до конфлікту з використанням у рядкових літералах Python такого ж символу з тією самою метою.

Скажімо, ви хочете написати регулярний вираз, відповідний \section, який потрібно знайти в LaTeX-файлі. Щоб з'ясувати, що написати в коді програми, почнемо з рядка, який потрібно зіставити. Далі, ви повинні уникнути будь-яких бекслешів та інших метасимволів, екранувавши їх зворотною косою рисою, в результаті чого в рядку з'являється частина . Тоді, результуючий рядок, який повинен бути переданий re.compile () повинен бути \section . Однак, для того, щоб висловити це як рядковий літерал Python, обидва бекслеші повинні бути екрановані знову, тобто "Section" .

Одним словом, щоб зіставити бекслеш, потрібно писати як рядок регулярного виразу '\', тому що регулярне вираз має бути \, і кожна зворотна коса риса повинна бути переведена в звичайний рядок як \.

Рішення полягає у використанні для регулярних виразів «сирих» рядків (raw string); у рядкових літералах з префіксом 'r' слеші ніяк не обробляються, так що r"\n" це рядок із двох символів ('\' та 'n'), а "\n" — з одного символу нового рядка. Тому регулярні вирази часто записуватимуться з використанням сирих рядків.

Regular StringRaw string
'ab*'r'ab*'
'\section'r'\section*'
'\w+\s+\1'r'w+s+1'

Виконання зіставлень

Після того, як у вас є об'єкт, що представляє регульоване скомпільоване вираз, що ви з ним робитимете? Об'єкти шаблонів мають кілька методів та атрибутів. Тільки найважливіші з них будуть розглянуті тут; Щоб отримати повний список, ознайомтеся з документацією re .

Метод/атрибутЦіль
match()Визначити, чи починається збіг регулярного виразу з початку рядка
search()Сканувати весь рядок у пошуках усіх місць збігів із регулярним виразом
findall()Знайти всі підрядки збігів з регулярним виразом та повернути їх у вигляді списку
finditer()Знайти всі підрядки збігів з регулярним виразом та повернути їх у вигляді ітератора

Якщо не було знайдено жодного збігу, match() і search() повертають None . Якщо пошук успішний, повертається екземпляр MatchObject , що містить інформацію про збіг: де воно починається і закінчується, підряд відповідності, і так далі.

Ви можете дізнатися про це, інтерактивно поекспериментувавши з модулем re. Ви також можете подивитися на Tools/scripts/redemo.py , демонстраційну програму, включену до дистрибутиву Python. Вона дозволяє вводити регулярні вирази та рядки, і відображає, чи є збіг з регулярним виразом чи ні. redemo.py може бути дуже корисною для налагодження складних регулярних виразів. Kodos Філа Шварца – ще один інтерактивний інструмент для розробки та тестування моделей РВ.

У цьому посібнику ми використовуємо для прикладів стандартний інтерпретатор Python:

Тепер можна спробувати порівняти рядки для регулярного вираження [a-z]+ . Порожній рядок йому не відповідатиме, тому що + означає повторення «один або більше» разів. match() у цьому випадку повинен повернути None , що й бачимо:

Тепер спробуємо рядок, який повинен збігтися із шаблоном: 'tempo' . У цьому випадку match() поверне MatchObject , який ви можете розмістити в якійсь змінній, щоб використовувати її надалі:

Тепер ви можете викликати MatchObject для отримання інформації про відповідні рядки. Для MatchObject також є кілька методів та атрибутів, найбільш важливими з яких є:

Метод/атрибутЦіль
group()Повернути рядок, що зійшовся з регулярним виразом
start()Повернути позицію початку збігу
end()Повернути позицію кінця збігу
span()Повернути кортеж (start, end) позицій збігу

Так як метод match() перевіряє збіги тільки з початку рядка, start() завжди повертатиме 0. Однак метод search() сканує весь рядок, так що для нього початок не обов'язково в нулі:

>>> print p. match ('.message')
None
>>> m = p. search('.message'); print m
< _sre. SRE_Match object at 0x. >
>>> m. group ( )
'message'
>>> m. span ( )
( 4 , 11 )

У реальних програмах найпоширеніший стиль це зберігання MatchObject у змінній, а потім перевірка по None. Зазвичай це виглядає так:

p = re. compile ( . )
m = p. match ( 'string goes here' )
if m:
print 'Match found: ', m. group ( )
else :
print 'No match'

Два методи повертають усі збіги шаблону. findall() повертає список підрядків, що збіглися:

>>> p = re. compile ( '\d+')
>>> p. findall ( '12 drummers drumming, 11 pipers piping, 10 lords a-leaping' )
[ ’12’ , ’11’ , ’10’ ]

Метод findall() повинен створити повний список, перш ніж він може бути повернутий як результат. Метод finditer() повертає послідовність екземплярів MatchObject як ітератора.

>>> iterator = p. finditer ( '12 drummers drumming, 11. 10.')
>>> iterator
< callable-iterator object at 0x401833ac >
>>> for match in iterator:
. print match. span ( )

( 0 , 2 )
( 22 , 24 )
( 29 , 31 )

Функції лише на рівні модуля

Вам не обов'язково потрібно створювати об'єкти шаблонів та викликати їх методи; модуль re також надає функції верхнього рівня match(), search(), findall(), sub() тощо. Ці функції приймають самі аргументи, як і шаблонів, з рядком РВ як перший аргумент і також повертають None чи MatchObject .

>>> print re. match ( r 'From \s +' , 'Fromage amk' )
None
>>> re. match ( r 'From \s +' , 'From amk Thu May 14 19:12:10 1998' )

Ці функції просто створюють вам об'єкт шаблону і викликають відповідний метод. Вони також зберігають об'єкт у кеші, тому майбутні виклики з використанням того ж регулярного виразу будуть швидше.

Чи ви повинні використовувати ці функції або шаблони з методами? Це залежить від того, як часто буде використовуватися регулярне вираження та від вашого особистого стилю кодингу. Якщо регулярне вираження використовується тільки в одному місці коду, такі функції, ймовірно, більш зручні. Якщо програма містить багато регулярних виразів, або повторно використовує одні й самі в кількох місцях, буде доцільно зібрати всі визначення у одному місці, у розділі коду, який попередньо компілює все регулярні висловлювання. Як приклад із стандартної бібліотеки, ось шматок з xmllib.py :

ref = re.compile( . )
entityref = re.compile( . )
charref = re.compile(.)
starttagopen = re.compile(.)

Сам я волію працювати зі скомпільованими об'єктами, навіть для одноразового використання, але мало хто виявиться таким же пуристом у цьому, як я.

Прапори компіляції

Прапори компіляції дозволяють змінювати деякі аспекти, як працюють регулярні висловлювання.Прапори доступні в модулі під двома іменами: довгим, таким як IGNORECASE і коротким, в однолітерній формі, таким як I . Декілька прапорів можуть бути задані у формі двійкового АБО; наприклад, re.I | re.M встановлює прапори I та M.

DOTALL, S
Зіставлення, таке саме як '.' , тобто з будь-яким символом, але при включенні цього прапора, на розгляд додається і символ нового рядка.

IGNORECASE, I
Зіставлення без урахування регістру; Наприклад, [A-Z] буде відповідати і малим літерам, так що Spam буде відповідати Spam, spam, spAM і так далі.

LOCALE, L
Робить \w, \W, \b, \B, що залежать від локалізації. Наприклад, якщо ви працюєте з текстом французькою, і хочете написати \w+ для того, щоб знаходити слова, але \w шукає тільки символи з множини [A-Za-z] і не буде шукати 'é' або 'ç'. Якщо система налаштована правильно та вибрано французьку мову, 'é' також розглядатиметься як буква.

MULTILINE, M
(Метасимволи ^ і $ ще не були описані; вони будуть представлені трохи пізніше, на початку другої частини цього посібника.

Зазвичай ^ шукає відповідність тільки на початку рядка, а $ тільки в кінці безпосередньо перед символом нового рядка (якщо є). Якщо цей прапор вказано, ^ порівняння відбувається у всіх рядках, тобто і на початку, і відразу після кожного символу нового рядка. Аналогічно для $.

UNICODE, U
Робить \w, \W, \b, \B, \d, \D, \s, \S відповідними таблиці Unicode.

VERBOSE, X
Включає багатослівні (докладні) регулярні висловлювання, які можуть бути організовані ясніше і зрозуміліше.Якщо зазначений цей прапор, пробіли в рядку регулярного виразу ігнорується, крім випадків, коли вони є в класі символів або передує неекранований бекслеш; це дозволяє вам організувати регулярні вирази ясніше. Цей прапор також дозволяє поміщати в регулярні вирази коментарі, що починаються з '#', які ігноруватимуться двигуном.

Приклад того, як РВ стає значно простіше читати:

Без verbose це виглядало б так:

У наведеному вище прикладі була використана автоматична конкатенація Python рядкових літералів для розбивки РВ на більш дрібні частини, але, без пояснень це приклад важче зрозуміти, ніж версію за допомогою re.VERBOSE .

На цьому місці ми поки що завершимо наш розгляд. Раджу трохи відпочити перед другою половиною, що містить розповідь про інші метасимволи, методи розбиття, пошуку та заміни рядків та велику кількість прикладів використання регулярних виразів.

Related Posts