Чи можете ви навести кілька прикладів того, чому важко проаналізувати XML та HTML за допомогою регулярного вираження? [зачинено]


402

Я бачу одну помилку, яку люди знову і знову роблять, намагаючись розібрати XML або HTML за допомогою регулярного вираження. Ось кілька причин важкого розбору XML та HTML:

Люди хочуть трактувати файл як послідовність рядків, але це дійсно:

<tag
attr="5"
/>

Люди хочуть розглядати <або <тег як початок тегу, але подібні речі існують у дикій природі:

<img src="imgtag.gif" alt="<img>" />

Люди часто хочуть співставити початкові теги з кінцевими тегами, але XML та HTML дозволяють тегам містити себе (з якими традиційні регулярні вирази взагалі не можуть впоратися):

<span id="outer"><span id="inner">foo</span></span> 

Люди часто хочуть відповідати вмісту документа (наприклад, відомої проблеми "знайти всі номери телефонів на певній сторінці"), але дані можуть бути позначені (навіть якщо вони виглядають як звичайні при перегляді):

<span class="phonenum">(<span class="area code">703</span>)
<span class="prefix">348</span>-<span class="linenum">3020</span></span>

Коментарі можуть містити неправильно відформатовані або неповні теги:

<a href="foo">foo</a>
<!-- FIXME:
    <a href="
-->
<a href="bar">bar</a>

Які ще ви знайомі?


14
Веб-браузери мають сенс такого безладу мільйони разів на секунду, чи не може хтось створити клас парсерування веб-сторінок для нас просто смертних?
Джон Вінстанлі,

24
Джон, вони є. У Perl є багато HTML :: Парсер, HTML :: TreeBuilder і т. Д. Існує майже напевно один для вашого мови.
Час. Оуенс

12
Найкращою відповіддю, stackoverflow.com/a/1732454/135078 (Обережно Zalgo)
Келлі С. Французький

3
Є гарне пояснення, чому [ви не можете розібрати [X] HTML з regex] [1] [1]: stackoverflow.com/a/1732454/468725
Павел P

4
Ось хороше пояснення того, як ви, звичайно, можете розібрати HTML з візерунками , а також чому ви, ймовірно, не бажаєте цього робити.
tchrist

Відповіді:


260

Ось кілька цікавих дійсних XML для вас:

<!DOCTYPE x [ <!ENTITY y "a]>b"> ]>
<x>
    <a b="&y;>" />
    <![CDATA[[a>b <a>b <a]]>
    <?x <a> <!-- <b> ?> c --> d
</x>

І ця маленька пачка радості є дійсним HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" [
    <!ENTITY % e "href='hello'">
    <!ENTITY e "<a %e;>">
]>
    <title>x</TITLE>
</head>
    <p id  =  a:b center>
    <span / hello </span>
    &amp<br left>
    <!---- >t<!---> < -->
    &e link </a>
</body>

Не кажучи вже про весь розбір для браузера недійсних конструкцій.

Удачі, пітінг-геджекс проти цього!

EDIT (Jörg W Mittag): Ось ще один приємний фрагмент добре сформованого, дійсного HTML 4.01:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd"> 
<HTML/
  <HEAD/
    <TITLE/>/
    <P/>

6
XML? Там є кілька різних конструкцій, що клопітно? Внутрішній підмножина DTD? Це визначає нову & сутність; називається 'y', що містить послідовність ']>', яка зазвичай, якщо не в лапках, закінчується внутрішнім підмножиною.
bobince

16
(Це свідчить про те, що ви повинні мати досить глибокі знання про деякі більш езотеричні та архаїчні особливості DTD XML, щоб правильно розібрати документ, навіть якщо ви не аналізатор, що підтверджує DTD.)
bobince

17
У прикладах HTML використовується рідко відома функція: shorttags. Детальніше читайте на w3.org/QA/2007/10/shorttags.html
netvope

25
Кожен раз, коли хтось пише HTML, як показано вище, Тім Бернерс-Лі проливає одну сльозу.
fgysin відновити Моніку

5
Мені подобається, як підсвітка синтаксису Stackoverflow виходить з ладу під час першого "" ".
GlassGhost

71

Насправді

<img src="imgtag.gif" alt="<img>" />

не є дійсним HTML, а також недійсним XML.

Це недійсний XML, оскільки '<' і '>' не є дійсними символами всередині рядків атрибутів. Їх потрібно уникнути, використовуючи відповідні об'єкти XML & lt; і & gt;

Це також недійсний HTML, оскільки форма короткого закриття не дозволена в HTML (але є правильною у XML та XHTML). Тег 'img' також є неявно закритим тегом відповідно до специфікації HTML 4.01. Це означає, що закриття вручну насправді неправильно, і еквівалентно закриттю будь-якого іншого тегу двічі.

Правильна версія в HTML - це

<img src="imgtag.gif" alt="&lt;img&gt;">

і правильна версія в XHTML та XML є

<img src="imgtag.gif" alt="&lt;img&gt;"/>

Наступний приклад, який ви навели, також недійсний

<
tag
attr="5"
/>

Це недійсний HTML або XML. Назва тегу має бути прямо за пунктом '<', хоча атрибути та закриття '>' можуть бути де вони хочуть. Тож дійсна XML насправді

<tag
attr="5"
/>

І ось ще одна прикольна: ви насправді можете вибрати як "або" як свій атрибут символу, що цитує

<img src="image.gif" alt='This is single quoted AND valid!'>

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

Редагувати: І навіть stackoverflow.com погоджується зі мною щодо визначення дійсного та недійсного. Ваш недійсний XML / HTML не виділяється, в той час як моя виправлена ​​версія є.

В основному, XML не робиться для розбору з регулярними виразами. Але для цього також немає підстав. Є багато, багато XML-аналізаторів для кожної мови. Ви можете вибрати між SAX-парсерами, DOM-парсерами та Pull-парсерами. Все це гарантується набагато швидше, ніж розбір з регулярним виразом, і ви можете потім використовувати круті технології, такі як XPath або XSLT на отриманому дереві DOM.

Отже, моя відповідь: не тільки важкий аналіз XML за допомогою regexps, але це також погана ідея. Просто скористайтеся одним з мільйонів існуючих парсерів XML і скористайтеся всіма розширеними функціями XML.

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


8
Вам не потрібно бігти> як> хоч.
Joey

8
Гаразд, s / дійсний / існує в дикій природі / г
Chas. Оуенс

1
Насправді, відповідно до специфікації, ви повинні втекти> як> так само, як ви повинні втекти <як <& і & amp; а в атрибутах "як" і "як" це саме стільки парсерів
LordOfThePigs

19
У специфікації не сказано, що ">" не слід уникати, за винятком окремого випадку послідовності "]]>" у змісті. З цієї причини найпростіше завжди втекти '>', але це не вимагає спец.
bobince

8
>знак цілком припустимо в HTML stackoverflow.com/questions/94528 / ...
JFS

56

На цю тему я написав цілий запис у блозі: Регулярні вислови обмеження

Суть проблеми полягає в тому, що HTML і XML є рекурсивними структурами, для правильного розбору потрібні механізми підрахунку. Справжній виразник не здатний рахувати. Ви повинні мати граматику без контексту, щоб рахувати.

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


20

У вашому списку не вказано, що атрибути можуть з’являтися в будь-якому порядку, тому якщо ваш регулярний вираз шукає посилання на href "foo" і клас "bar", вони можуть приймати в будь-якому порядку і мати будь-яку кількість інших речі між ними.


Ага, так, це навіть питання, яке змусило мене задати це питання (перша посилання).
Час. Оуенс

16

Це залежить від того, що ви маєте на увазі під "розбором". Взагалі кажучи, XML не можна аналізувати за допомогою регулярного вираження, оскільки граматика XML аж ніяк не є регулярною. Простіше кажучи, регулярні вирази не можуть підрахувати (ну, Perge-регекси насправді можуть рахувати речі), тому ви не можете врівноважувати теги з відкритим закриттям.


Я здогадуюсь, що зворотні посилання можуть вирішити проблему відкритих та закритих тегів
Rishul Matta

1
@RishulMatta: як? У вас є лише обмежена кількість зворотних посилань, і ви маєте на увазі, що вам потрібно змінити теги ... Крім того, чітке визначення регулярних виразів не дозволяє зворотних посилань.
Віллем Ван Онсем

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

9

Чи справді люди роблять помилку, використовуючи регулярний вираз, чи це просто досить добре для завдання, яке вони намагаються досягти?

Я повністю погоджуюся, що розбір html та xml за допомогою регулярного вираження неможливий, як відповіли інші.

Однак, якщо ваша вимога полягає не в тому, щоб проаналізувати html / xml, а просто отримати один невеликий біт даних у "відомому доброму" біті html / xml, то, можливо, регулярний вираз або навіть ще простіша "підрядка" є досить хорошою.


7
Визначте "досить добре". Неминуче простий регулярний вираз не працюватиме. Це не відповідність чи що-небудь, що не має помилки? Якщо так, то використання регулярних виразів є помилкою. HTML і XML-аналізатори не важкі у використанні. Уникати їх навчання - помилкова економія.
Час. Оуенс

1
добре, визначте "досить добре". Скажімо, у мене є веб-сторінка, яка повідомляє мені IP-адресу клієнтів. Це все, що робить. Тепер мені потрібно написати заявку на машину клієнтів, яка повідомляє мені її IP-адресу. Я заходжу на цей сайт, шукаю IP-адресу та повертаю її. Розбір HTML не потрібен!
День Робіна

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

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

@Robert: "майже немає випадків використання" - це перебільшення. На мій досвід, є випадки використання, які досить поширені. ЯГНІ застосовується тут ... іноді. Хитрість полягає в тому, щоб знати, яким має бути куленебезпечне та довговічне рішення для конкретного завдання, яке ви вирішуєте. Робін має хороший момент. Він лише каже, що повний аналіз XML не завжди того вартий ... що вірно, навіть якщо ви знаєте, як ним користуватися.
LarsH

6

Люди зазвичай замовчують писати жадібні візерунки, що досить часто призводить до непродуманого. * Перекочування великих шматочків файлів у найбільший можливий <foo>. * </foo>.


2
Окрім того, щоб зробити повторення лінивим .*?<, ви можете це виправити, використовуючи заперечений клас символів, як [^<]*<. (Відмова від відповідальності: очевидно, це все ще не є надійною, що полягає в питанні.)
Рорі О'Кайн

6

Мені спокуса сказати "не заново вигадуйте колесо". За винятком того, що XML - це дійсно, дуже складний формат. Тож, можливо, я повинен сказати "не винаходити синхротрон".

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

Тому що розбір XML важкий . Будь-які зусилля, які ви економите, не навчившись використовувати бібліотеку для розбору XML, будуть більш ніж виграні кількістю творчих робіт та розладів помилок, які вам доведеться зробити. Для себе перейдіть на Google "XML-бібліотека" і використовуйте чужу роботу.


3
Хоча це не так складно, як C ++.
Коул Джонсон

6
@Cole "Cole9" Джонсон, я також не використовувавму REs для розбору C ++.
Ісаак Рабінович

2
Якщо XML є синхротроном, C ++ буде Великим адронним колайдером.
Кевін Костлан

4

Я вважаю, що ця класика має ту інформацію, яку ви шукаєте. Ви можете знайти точку в одному з коментарів там:

Я думаю, що тут недоліком є ​​те, що HTML - це граматика Chomsky Type 2 (граматика без контексту), а RegEx - граматика Chomsky Type 3 (регулярний вираз). Оскільки граматика типу 2 принципово складніша, ніж граматика типу 3 - ви, можливо, не можете сподіватися на те, щоб зробити цю роботу . Але багато хто спробує, деякі претендуватимуть на успіх, а інші знайдуть провину і цілком зіпсують вас.

Ще трохи інформації з Вікіпедії: Ієрархія Хомського


6
"Регулярне вираження" не має точно такого ж значення в формальних дискусіях з граматики, як це робиться тут. Більшість існуючих двигунів з регулярними виразками є більш потужними, ніж граматики Хомського типу 3 (наприклад, не жадібна відповідність, зворотна зміна). Деякі двигуни-регекси (наприклад, Perl) закінчують Тьюрінг. Це правда, що навіть це погані інструменти для розбору HTML, але цей цитований часто аргумент не є причиною.
сумнівним

4

Я думаю, що проблеми зводяться до:

  1. Регекс майже незмінно неправильний. Є законні дані, які не зможуть відповідати правильно. Якщо ви працюєте досить наполегливо, ви можете зробити це на 99% правильно, або 99,999%, але зробити це на 100% правильно майже неможливо, хоч би через дивні речі, які дозволяє XML, використовуючи сутності.

  2. Якщо регулярний вираз невірний, навіть для 0,00001% введених даних, у вас є проблема безпеки, оскільки хтось може виявити той вхід, який порушить вашу програму.

  3. Якщо регулярний вираз буде достатньо правильним, щоб охопити 99,99% випадків, він буде цілком нечитабельним і неможливим.

  4. Цілком ймовірно, що регулярний вираз буде працювати дуже погано для вхідних файлів середнього розміру. Моя перша зустріч із XML полягала в тому, щоб замінити скрипт Perl, який (неправильно) проаналізував вхідні документи XML на належний аналізатор XML, і ми не лише замінили 300 рядків нечитабельного коду на 100 рядків, які кожен міг зрозуміти, але ми покращили час відгуку користувачів від 10 секунд до приблизно 0,1 секунди.


1

Взагалі кажучи, XML не можна аналізувати за допомогою регулярного вираження, оскільки граматика XML аж ніяк не є регулярною. Простіше кажучи, регулярні вирази не можуть підрахувати (ну, Perge-регекси насправді можуть рахувати речі), тому ви не можете врівноважувати теги з відкритим закриттям.

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

Тут я показав приклад регулярного вираження, щоб уникнути розбору помилок прикладів у першому повідомленні.


По-перше, рекурсивні регекси не є регулярними виразами (якщо ви подивитеся в круглі дужки, ви побачите, що я визнаю, що регекси Perl, які є рекурсивними, можуть рахувати речі, необхідні для обробки HTML). По-друге, ваш приклад - це добре сформований XHTML або XML. HTML не сформований. По-третє, ви повинні запитати себе, чи простіше розширити і підтримувати парсер, написаний рекурсивною мовою регулярних виразів або загальною мовою програмування.
Час. Оуенс

По-четверте, навіть ваш приклад тривіально порушений, поки він є дійсним XML. Додайте один пробіл між content_block та id, і він не вдається. Я впевнений, якби витратив ще кілька хвилин, я знайшов би іншу структурну помилку у вашому коді. Це просто не дуже гарна ідея.
Час. Оуенс

1

Я дав спрощений відповідь на це питання тут . Хоча це не враховує 100% позначку, я пояснюю, як це можливо, якщо ви готові виконати якусь попередню роботу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.