Регулярні вирази для чайників

  1. Про регулярні вирази
  2. Вчимося розуміти регулярні вирази
  3. Ледачі і Жадібні квантіфікатори
  4. Уніфікуємо регулярний вираз.
  5. Посилання в регулярних виразах
  6. Приклад парсинга інформації

Здебільшого 80% сучасного програмування зведені до аналізу і роботі з інформацією. А інформація завжди надається нам в текстовій формі, просто тому, що інші її форми аналізу так легко не піддаються. Простіше кажучи одна з перших завдань, з якою стикається будь-який програміст, це парсинг інформації. А де парсинг, там і регулярні вирази. Про них ми сьогодні і поговоримо бо з'ясувалося, що багато понять не мають, що це таке, і з чим це їдять. Після прочитання цієї статті ви зрозумієте як створювати свої регулярні вирази, а так-же як з ними працювати. Нижче навіть приклад на php є.

Про регулярні вирази

І так, що ж таке регулярний вираз?

/<a\s*href='(.+?)'/

Я, таки, можу прямо сюди скопіювати опис з вікіпедії, але думаю краще просто показати, бо пояснити як воно працює на словах дуже складно. Тут треба зрозуміти. Причому самому. Але визначення я все-таки дам:

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

Що дозволяють робити ці «магічні конструкції» і чому так важливо вміти їх використовувати? Насправді все просто.
Припустимо, нам треба знайти всі теги <div> в рядку. Пишемо регулярний вираз, скармливаем йому рядок і все!

preg_match ( "/ <div [^>] *> (. +?) <\ / div> / is", $ data, $ matches);

Початківець кодер, таки, може запитати: а хіба то-ж саме можна зробити через strpos і substr? Так, це можна зробити, але давайте просто порівняємо кількість коду:

$ Spos = stripos ($ html, "<div"); $ Lpos = stripos ($ html, "</ div"); if ($ spos! == false && $ lpos! == false) $ result = substr ($ spos, $ spos- $ lpos);

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

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

Перш ніж щось писати або пояснювати, я б хотів представити вам інструмент для роботи з регулярки, це RegExr . Відкрийте його в новій вкладці і опрацьовуйте там все приклади, які я буду приводити.
Почнемо з простого, з чого починав я сам. З отримання вмісту атрибута тега. Припустимо нам треба взяти атрибут href = "посилання" з тега посилання. Як нам це зробити?
Давайте відкриємо ide RegExr і в нижньому полі впишемо html код посилання.

Тепер власне складемо регулярку. Що ми знаємо?
1. Атрибут починається з href і містить знак одно, з двома лапками по краях.
2. Потрібні нам дані знаходяться між цих лапок і ці дані можуть змінюватися випадковий чином

Пункт 1 вирішується вкрай просто. Давайте вкажемо в верхньому полі відомі нам дані:

Давайте вкажемо в верхньому полі відомі нам дані:

Тепер власне звернемося до пункту номер 2.

Як вказати рядок, яка змінюється випадковим чином? Щоб зрозуміти рядок, треба зрозуміти з чого вона складається. (c) Будь-випадкова рядок складається з літер і (або) символів, які вказані в ній в різному (будь-якому) порядку. Щож, відкриємо довідник регулярних виразів. Кращий з зустрінутих мною, це довідник від microsoft . Не дивіться, що там зазначено, нібито довідник для javascript. Здебільшого регулярні вирази працюють однаково на будь-яких мовах від C # до php. Шукаємо там опис коду для будь-якого символу:

Як ми бачимо, в регулярке за будь-який символ відповідає точка (.), Звичайнісінька чорна точка. Ось її ми і вкажемо після href = ""

Нічого? Так, все вірно, регулярка не працює, (очікувано). А все тому, що ми вказали 1 символ. Тобто, якщо вказати в атрибуті href всього 1 символ, то він вираз спрацює. Давайте переконаємося в цьому:

Тепер перед нами стоїть завдання вказати інтерпретатору, що на них не завжди більше 1 в кілька разів. Тут є кілька різних шляхів, але зупинимося на одному. Це банальне повторення попереднього символу потрібну кількість разів. Шукаємо в довіднику по посиланню вище інформацію про повторення.

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

Ура! Запрацювало. Таким чином можна сказати ми написали наше перше регулярне вираз. Але не будемо поспішати, бо немає межі досконалості. Припустимо, що нам треба отримати з тексту саме ту частину яка знаходиться між лапками. Чи робить це наше вираз? Ні. Воно отримує повністю весь блок. Щоб переконатися наведемо на миша на виділений текст:

Тобто завдання ще не виконано. Знову відкриваємо довідник і шукаємо як захопити частину регулярного виразу.

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

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

В php ця регулярка переноситься так:

/href="(.*)"/

Ледачі і Жадібні квантіфікатори

Власне різниця між цими квантіфікаторамі в підході до вибірки даних. Жадібне квантіфікатори беруть Максимально можливий текстовий блок. А Ледачі хапають те, до чого швидше за все дотягуються. Бо решта збирати - «лінь».
У нашому випадку ліниве регулярний вираз виглядає так:

/href="(.*?)"/

Я додав знак питання після зірочки. читаємо:

Щоб висвітлити цю тему на прикладі нам доведеться відкрити php sandbox , Яка дозволяє запускати php код прямо з браузера.

Код який ми будемо запускати наведено нижче:

// дублюємо двічі нашу посилання $ str = '<a href="http://aftamat4ik.ru"> Це посилання з якої ми будемо брати href </a> <a href="http://aftamat4ik.ru"> це посилання з якої ми будемо брати href </a> '; // Жадібно preg_match ( '/ href = "(. *)" / Is', $ str, $ matches); echo "Жадібне регулярний вираз:". $ matches [1] .PHP_EOL ;; echo '-------------------------'. PHP_EOL; // Ліниво preg_match ( '/ href = "(. *?)" /', $ Str, $ matches); echo "Ледаче регулярний вираз:". $ matches [1] .PHP_EOL;

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

/href="(.*?)"/

Але це ще не все!

Уніфікуємо регулярний вираз.

Перемкнемося назад до RegExr і представимо ситуацію, при якій html код, аналізований нами, містить в href не тільки подвійні, а й одинарні лапки.
Перемкнемося назад до RegExr і представимо ситуацію, при якій html код, аналізований нами, містить в href не тільки подвійні, а й одинарні лапки

Не працює! Відповідний спосіб вирішення - це вказати замість лапок, набір символів, де будуть обидва види цих елементів. За створення набору символів відповідають квадратні дужки:

За створення набору символів відповідають квадратні дужки:

Власне робиться це так: [ " '] - ця послідовність символів відповідає як" і так і'.
Підсумкове регулярний вираз набуде вигляду:

/href=['"](.*?)['"]/

Як ви бачите, все визначається правильно Як ви бачите, все визначається правильно. Але, зверніть увагу на останню посилання, де є href = 'і ", тобто абсолютно не працює html код, який, тим не менш, визначається як працює. Що робити, якщо його треба виключити? Використовуємо посилання.

Посилання в регулярних виразах

Посилання дозволяють нам, в самому вираженні, посилатися на його елементи. Наприклад наш елемент - href = "" має два повторюваних символу, на які ми і будемо спиратися. Це "і" або 'і'. Так як вони повторюються, ми можемо взяти такий символ і послатися на нього вдруге, а не вказувати знову. Тоді вийде відфільтрувати помилку, адже якщо ми посилаємося на лапки виду "то регулярний вираз буде шукати саме її. Щоб послатися, треба просто вказати конструкцію виду: \ число, наприклад \ 1 де цифра - номер скобочки. Так, елемент на який ми посилаємося , повинен бути в круглих дужках. у нашому випадку:
Беремо вираз [ ' "] і обертаємо в лапки (['"]) а потім просто посилаємося на лапки через \ 1
В результаті отримаємо:

/href=(['"])(.*?)/

)/

Ура! Останній рядок не враховується.

У 90% випадків цих знань вам вистачить з лишком для парсинга чого завгодно, звідки завгодно.

Приклад парсинга інформації

Давайте, використовуючи регулярні вирази і php, Спарс, в маленькому скрипті, наприклад, цю сторінку - Хабр . Для відпрацювання навичок саме воно буде.

В php за роботу регулярок відповідають функції preg_match і preg_match_all, які і будуть використовуватися в даному прикладі.

Для початку, давайте визначимо регулярний вираз, яке відповідає заголовку статті.

Я працюю в браузері гугл хром, тому відкриваю html-код сторінки і шукаю там блок з матеріалом.

і далі сюди -

Тут виділено блок, який відповідає за назву матеріалу, яке ми і будемо аналізувати довільні в першу чергу. Щож, беремо його, копіюємо його в regexr і підбираємо регулярний вираз!

Як ви бачите в Group # 1 знаходиться чистий заголовок. Розберемо то, що я написав у вираженні:

/class=['"]post__title-arrow.+&lt;\/span>[\n\s\t]+?<span>(.+?)<\/span>/

Елемент (. +?), Що представляє собою рядок не нульовий довжини, обрамлений спереду в рядок з атрибутом class містить блок post__title-arrow та будь-які. + Символи після цього блоку, а так-же два елементи </ span> і <span> , розділені між собою послідовністю з розриву рядків, таба і пробілу повторених кілька разів [\ n \ s \ t] +? закінчується на </ span> Так ... опис роботи виглядає як ... в загальному не намагайтеся це повторити.

preg_match ( "/ class = [ '\"] post__title-arrow. + &lt;\ / span> [\ n \ s \ t] +? <span> (. +?) <\ / span> / ", $ html, $ titles);

До речі в блоці [ '\ "] символ \ означає екранування лапки. Він не враховується в підсумковому виразі і всього-лише допомагає інтерпретатора сприймати рядок цілком. Залишилося вставити цю регулярку в php з екрануванням і обрамленням в символи кордонів.

Підсумковий php код виглядає так:

$ Html = file_get_contents ( "https://habrahabr.ru/sandbox/15940/"); preg_match ( "~ class = [ '\"] post__title-arrow. + &lt;\ / span> [\ n \ s \ t] +? <span> (. +?) <\ / span> ~ ", $ html, $ titles); echo $ titles [1];

Функція preg_match аналізує рядок $ html і шукає там блок, який підходить під регулярний вираз, після чого витягує з цього блоку групи в круглих дужках () і поміщає їх в масив. В даному випадку цей масив називається $ titles і $ titles [1] власне висновок першої (і єдиною в даному випадку) лапки.
І результат виконання коду виглядає так:

І результат виконання коду виглядає так:

Точно так-же визначаємо регулярний вираз для тексту статті. Знову копіює блок відповідає за даний текст в regexr і підбираємо вираз, а потім просто вставляємо його в preg_match з екрануванням.

Знову копіює блок відповідає за даний текст в regexr і підбираємо вираз, а потім просто вставляємо його в preg_match з екрануванням

І вираз:

/ Class = [ ' "] content html_format ['"]> ([\ s \ S] +?) <Div class = [ ' "] post__tags /

Тут ви бачите новий блок [\ s \ S] + ?, що він означає? Заголовок статті - не має в собі переносів рядка і тому там можна було впоратися банальним повторенням символу. +? тут же у нас є переноси і прогалини. Бо це контент. Щоб вирішити проблему ми просто поміщаємо всередину лапок символ \ s - пробілу і \ S - все що завгодно, крім пробілу і вказуючи, що цей блок повторюється кілька разів через +?

Результат парсинга буде такий -

І ось ми Спарс сайт! Так, так це працює. Людина, який витратив пару днів на вивчення регулярок, надалі зможе Спарс все що завгодно!

Ну і на останок, зрозуміло, загальний код.

// Тестовий парсер header ( 'Content-Type: text / html; charset = utf-8'); // Визначаємо заголовок матеріалу $ html = file_get_contents ( "https://habrahabr.ru/sandbox/15940/"); preg_match ( "~ class = [ '\"] post__title-arrow. + &lt;\ / span> [\ n \ s \ t] +? <span> (. +?) <\ / span> ~ ", $ html, $ titles); preg_match ( "~ class = [ '\"] content html_format [' \ "]> ([\ s \ S] +?) <div class = [ '\"] post__tags ~ ", $ html, $ contents); echo "<h3>". $ titles [1]. "</ h3>"; echo $ contents [1];

З невеликими відмінностями, регулярні вирази однакові скрізь. У js, php, c # або будь-якому іншому мовою програмування. Синтаксис всюди один і той-же, а значить і цей метод буде актуальний і там теж. До слова, не дивлячись на всі свистопляски з regexr, які я тут описав, досвідчений кодер здатний написати регулярку на льоту, не заморочуючись. Вчіться, хехе.
Ну і, зрозуміло, ось исходник, якщо вам цікаво:

схоже

Що дозволяють робити ці «магічні конструкції» і чому так важливо вміти їх використовувати?
Як нам це зробити?
Що ми знаємо?
Чи робить це наше вираз?
Що робити, якщо його треба виключити?
Lt;\/span>[\n\s\t]+?
Символи після цього блоку, а так-же два елементи </ span> і <span> , розділені між собою послідовністю з розриву рядків, таба і пробілу повторених кілька разів [\ n \ s \ t] +?
Lt;\ / span> [\ n \ s \ t] +?
Lt;\ / span> [\ n \ s \ t] +?
О він означає?