Czy możesz podać kilka przykładów, dlaczego trudno jest analizować XML i HTML za pomocą wyrażenia regularnego? [Zamknięte]

402

Jeden błąd widzę ludzi co nad i znowu próbuje zanalizować XML lub HTML z regex. Oto kilka powodów, dla których parsowanie XML i HTML jest trudne:

Ludzie chcą traktować plik jako ciąg wierszy, ale jest to poprawne:

<tag
attr="5"
/>

Ludzie chcą traktować tag <lub <jako początek tagu, ale takie rzeczy istnieją na wolności:

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

Ludzie często chcą dopasowywać tagi początkowe do tagów końcowych, ale XML i HTML pozwalają tagom się zawierać (których tradycyjne wyrażenia regularne w ogóle nie są w stanie obsłużyć):

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

Ludzie często chcą dopasować do treści dokumentu (np. Słynny problem „znajdź wszystkie numery telefonów na danej stronie”), ale dane mogą zostać oznaczone (nawet jeśli wyglądają normalnie podczas przeglądania):

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

Komentarze mogą zawierać źle sformatowane lub niekompletne tagi:

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

O jakich innych problemach jesteś świadomy?

Chas. Owens
źródło
14
Przeglądarki internetowe wyczuwają ten bałagan miliony razy na sekundę, czy ktoś nie może stworzyć klasy parsera stron internetowych dla nas zwykłych śmiertelników?
Jon Winstanley
24
Jon mają. W Perlu jest wiele HTML :: Parser, HTML :: TreeBuilder itp. Niemal na pewno jest jeden dla twojego języka.
Chas. Owens
12
Najlepsza odpowiedź to: stackoverflow.com/a/1732454/135078 (Uwaga: Zalgo)
Kelly S. Francuski
3
Istnieje dobre wytłumaczenie, dlaczego [nie można parsować [X] HTML za pomocą wyrażenia regularnego] [1] [1]: stackoverflow.com/a/1732454/468725
Pavel P
4
Oto dobre wyjaśnienie, w jaki sposób z pewnością możesz parsować HTML ze wzorami , a także dlaczego prawdopodobnie tego nie chcesz.
tchrist

Odpowiedzi:

260

Oto kilka poprawnych poprawnych plików XML:

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

A ten mały pakiet radości jest poprawnym 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>

Nie wspominając już o parsowaniu specyficznych dla przeglądarki nieprawidłowych konstrukcji.

Powodzenia przeciwko temu wyrażeniu!

EDYCJA (Jörg W Mittag): Oto kolejny fajny kawałek dobrze sformułowanego, poprawnego HTML 4.01:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd"> 
<HTML/
  <HEAD/
    <TITLE/>/
    <P/>
Bobin
źródło
6
Ten XML? Jest tam kilka różnych konstrukcji, co jest kłopotliwe? Wewnętrzny podzbiór DTD? To definiuje nowy & byt; zwany „y”, zawierający sekwencję „]>”, która normalnie, jeśli nie w cudzysłowach, kończy wewnętrzny podzbiór.
bobince
16
(To pokazuje, że musisz mieć dość głęboką wiedzę na temat niektórych bardziej ezoterycznych i archaicznych funkcji DTD XML, aby poprawnie parsować dokument, nawet jeśli nie jesteś parserem zatwierdzającym DTD.)
Bob
17
Przykłady HTML wykorzystują rzadko znaną funkcję: skróty. Czytaj więcej na w3.org/QA/2007/10/shorttags.html
netvope
25
Za każdym razem, gdy ktoś pisze HTML, jak pokazano powyżej, Tim Berners-Lee zrzuca jedną łzę.
fgysin przywraca Monikę
5
Uwielbiam, jak wyróżnienie składni Stackoverflow zawodzi przy pierwszym wystąpieniu „]”.
GlassGhost
71

Tak właściwie

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

nie jest poprawnym HTML-em i nie jest poprawnym XML-em.

To nie jest poprawny XML, ponieważ „<” i „>” nie są prawidłowymi znakami w ciągach atrybutów. Należy je zmienić za pomocą odpowiednich jednostek XML & lt; i>

Nie jest to również poprawny HTML, ponieważ krótki formularz zamykający nie jest dozwolony w HTML (ale jest poprawny w XML i XHTML). Znacznik „img” jest także znacznikiem niejawnie zamkniętym zgodnie ze specyfikacją HTML 4.01. Oznacza to, że ręczne zamknięcie jest w rzeczywistości nieprawidłowe i jest równoważne dwukrotnemu zamknięciu dowolnego innego znacznika.

Prawidłowa wersja w HTML to

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

a poprawna wersja w XHTML i XML to

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

Podany poniżej przykład jest również nieprawidłowy

<
tag
attr="5"
/>

To też nie jest poprawny HTML ani XML. Nazwa znacznika musi znajdować się tuż za „<”, chociaż atrybuty i zamykające „>” mogą znajdować się w dowolnym miejscu. Tak więc poprawny XML jest w rzeczywistości

<tag
attr="5"
/>

A oto jeszcze jedna zabawniejsza: możesz faktycznie użyć „lub” jako znaku cudzysłowu

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

Wszystkie pozostałe powody, które zostały opublikowane, są poprawne, ale największym problemem podczas analizowania HTML jest to, że ludzie zwykle nie rozumieją poprawnie wszystkich reguł składni. Fakt, że twoja przeglądarka interpretuje tagoupoup jako HTML, nie oznacza, że ​​faktycznie napisałeś prawidłowy HTML.

Edycja: I nawet stackoverflow.com zgadza się ze mną w sprawie definicji ważnej i niepoprawnej. Twój nieprawidłowy XML / HTML nie jest podświetlony, a moja poprawiona wersja to.

Zasadniczo XML nie jest analizowany za pomocą wyrażeń regularnych. Ale nie ma też powodu, aby to robić. Istnieje wiele, wiele parserów XML dla każdego języka. Masz wybór między parserami SAX, parserami DOM i parserami Pull. Wszystkie te są gwarantowane znacznie szybciej niż parsowanie z wyrażeniem regularnym, a następnie możesz użyć fajnych technologii, takich jak XPath lub XSLT, w powstałym drzewie DOM.

Moja odpowiedź brzmi zatem: nie tylko trudno jest analizować XML z wyrażeniami regularnymi, ale jest to również zły pomysł. Wystarczy użyć jednego z milionów istniejących parserów XML i skorzystać ze wszystkich zaawansowanych funkcji XML.

HTML jest po prostu zbyt trudny, aby nawet samemu parsować. Po pierwsze, składnia prawna zawiera wiele drobnych subtelności, o których być może nie jesteś świadomy, a po drugie, HTML na wolności jest po prostu ogromną śmierdzącą stertą (dostajesz mój dryf). Istnieje wiele bibliotek parserów Lax, które dobrze sobie radzą z obsługą HTML, takich jak zupa tagów, wystarczy ich użyć.

LordOfThePigs
źródło
8
Jednak nie musisz uciekać> jak>.
Joey,
8
Okej, s / valid / istnieje na wolności / g
Chas. Owens
1
Właściwie, zgodnie ze specyfikacją musisz uciec> as> tak samo, jak musisz uciec <as <& i & amp; oraz w atrybutach „as” i „as” to tylko tyle parserów
LordOfThePigs
19
W specyfikacji nie podano, że „>” musi być poprzedzone znakiem ucieczki - z wyjątkiem specjalnego przypadku sekwencji „]]>„ w treści. Z tego powodu najłatwiej jest zawsze uciec '>', ale nie jest to wymagane przez spec.
bobince
8
>znak jest całkowicie poprawny w html stackoverflow.com/questions/94528/…
jfs
56

Napisałem cały wpis na blogu na ten temat: Ograniczenia wyrażeń regularnych

Sedno problemu polega na tym, że HTML i XML są strukturami rekurencyjnymi, które wymagają mechanizmów zliczania w celu prawidłowego parsowania. Prawdziwe wyrażenie regularne nie jest w stanie zliczyć. Aby liczyć, musisz mieć gramatykę bezkontekstową.

Poprzedni akapit zawiera pewne zastrzeżenie. Niektóre implementacje wyrażeń regularnych obsługują teraz ideę rekurencji. Jednak gdy zaczniesz dodawać rekurencję do wyrażeń regularnych, naprawdę rozciągasz granice i powinieneś rozważyć parser.

JaredPar
źródło
20

Jednej nie ma na liście, że atrybuty mogą pojawiać się w dowolnej kolejności, więc jeśli wyrażenie regularne szuka linku z href „foo” i klasą „bar”, mogą występować w dowolnej kolejności i mieć dowolną liczbę innych rzeczy między nimi.

AmbroseChapel
źródło
Ach, tak, to było nawet pytanie, które skłoniło mnie do zadania tego (pierwszy link).
Chas. Owens,
16

To zależy od tego, co rozumiesz przez „parsowanie”. Ogólnie mówiąc, XML nie może być analizowany przy użyciu wyrażenia regularnego, ponieważ gramatyka XML nie jest bynajmniej regularna. Krótko mówiąc, wyrażenia regularne nie mogą się liczyć (no cóż, wyrażenia regularne Perla mogą być w stanie policzyć rzeczy), więc nie można zrównoważyć tagów otwieranie-zamykanie.

Anton Gogolev
źródło
myślę, że odniesienia wsteczne mogą rozwiązać problem z otwieraniem i zamykaniem tagów
Rishul Matta
1
@RishulMatta: jak? Masz tylko ograniczoną liczbę odsyłaczy zwrotnych i pamiętaj, że musisz odwrócić tagi ... Ponadto ścisła definicja wyrażeń regularnych nie pozwala na odsyłacze zwrotne.
Willem Van Onsem,
.NET pozwala na równoważenie wyrażeń, które pojawiają się i wypychają, i teoretycznie mogą być użyte do dopasowania hierarchii. Ale to wciąż zły pomysł.
Abel
9

Czy ludzie rzeczywiście popełniają błąd, używając wyrażenia regularnego, czy jest to po prostu wystarczająco dobre do zadania, które próbują osiągnąć?

Całkowicie się zgadzam, że analizowanie html i xml przy użyciu wyrażenia regularnego nie jest możliwe, ponieważ inni ludzie odpowiedzieli.

Jeśli jednak twoim wymaganiem nie jest parsowanie html / xml, ale tylko uzyskanie jednego małego fragmentu danych w „znanym dobrym” kawałku html / xml, być może wystarczające jest wyrażenie regularne lub nawet prostsze „podciąg”.

Robin Day
źródło
7
Zdefiniuj „wystarczająco dobry”. Nieuchronnie prosty regex nie zadziała. Czy nie pasuje coś lub nie pasuje błąd? Jeśli tak, to użycie wyrażeń regularnych jest błędem. Parsery HTML i XML nie są trudne w użyciu. Unikanie ich uczenia się jest fałszywą ekonomią.
Chas. Owens
1
ok, zdefiniuj „wystarczająco dobry”. Powiedzmy, że mam stronę internetową, która podaje mi adres IP klienta. To wszystko, co robi. Teraz muszę napisać aplikację na komputer kliencki, która powie mi swój adres IP. Wchodzę na tę stronę, szukam adresu IP i zwracam go. Analiza HTML nie jest potrzebna!
Robin Day
2
Jeśli masz dowolny ciąg znaków, którego format jest całkowicie pod twoją kontrolą, fakt, że ciąg znaków jest poprawnie sformatowanym kodem XML, naprawdę nie jest istotny. Ale prawie żadne przypadki użycia XML nie należą do tej kategorii.
Robert Rossney
15
Mogę ci powiedzieć z bolesnego doświadczenia, że ​​przez większość czasu można uzyskać to, czego chcesz, stosując absurdalne złożone wzorce wyrażeń regularnych. Dopóki strona nie ulegnie zabawnej drobnej zmianie, możesz wyrzucić ten regex, który sprawił, że płakałeś przez dwa dni za oknem i zaczynasz od nowa.
Thomasz
@Robert: „prawie żadnych przypadków użycia” to przesada. Z mojego doświadczenia wynika, że ​​przypadki użycia są dość powszechne. YAGNI stosuje się tutaj ... czasami. Sztuka polega na tym, aby wiedzieć, jak kuloodporne i długotrwałe musi być Twoje rozwiązanie dla konkretnego zadania, do którego się zmierzasz. Robin ma dobrą rację. Mówi tylko, że pełne parsowanie XML nie zawsze jest tego warte ... co jest prawdą, nawet jeśli wiesz, jak go używać.
LarsH,
6

Ludzie zwykle domyślnie piszą chciwe wzory, co często prowadzi do nierozważnego przemyślenia. * Dzielenie dużych kawałków pliku na możliwie największy <foo>. * </foo>.

chaos
źródło
2
Oprócz leniwego powtarzania .*?<, możesz to naprawić, używając negowanej klasy postaci, takiej jak [^<]*<. (Zastrzeżenie: oczywiście nadal nie jest to niezawodne, o to właśnie chodzi).
Rory O'Kane
6

Kusi mnie, aby powiedzieć „nie wymyślaj od nowa koła”. Tyle że XML jest naprawdę, naprawdę złożony format. Więc może powinienem powiedzieć „nie wymyślaj ponownie synchrotronu”.

Być może prawidłowa fraza zaczyna się „gdy wszystko, co masz, to młotek…”. Wiesz, jak używać wyrażeń regularnych, wyrażenia regularne są dobre w analizie, więc po co zawracać sobie głowę nauką biblioteki parsowania XML?

Ponieważ parsowanie XML jest trudne . Wszelkie wysiłki zaoszczędzone przez to, że nie musisz nauczyć się korzystać z biblioteki parsowania XML, będą więcej niż nadrobione przez pracę twórczą i usuwanie błędów, które będziesz musiał zrobić. Dla własnego dobra „Google Library” w Google i wykorzystaj czyjąś pracę.

Izaak Rabinowicz
źródło
3
Nie jest jednak tak skomplikowany jak C ++.
Cole Johnson
6
@Cole „Cole9” Johnson Nie użyłbym również RE do parsowania C ++.
Izaak Rabinovitch
2
Jeśli XML jest synchrotronem, C ++ byłby dużym zderzaczem hadronów.
Kevin Kostlan
4

Wierzę, że ten klasyk zawiera informacje, których szukasz. Można znaleźć punkt w jednym z komentarzy tam:

Myślę, że wadą jest to, że HTML to gramatyka Chomsky'ego typu 2 (gramatyka bez kontekstu), a RegEx to gramatyka Chomsky'ego typu 3 (wyrażenie regularne). Ponieważ gramatyka typu 2 jest zasadniczo bardziej złożona niż gramatyka typu 3 - nie możesz oczekiwać, że to zadziała . Ale wielu będzie próbowało, niektórzy twierdzą, że sukces, a inni znajdą winę i całkowicie zepsują.

Więcej informacji z Wikipedii: Chomsky Hierarchy

Adam Arold
źródło
6
„Wyrażenie regularne” nie ma dokładnie tego samego znaczenia w formalnych dyskusjach gramatycznych, jak tutaj. Większość istniejących silników wyrażeń regularnych ma większą moc niż gramatyka Chomsky'ego typu 3 (np. Niepochodne dopasowywanie, odnośniki zwrotne). Niektóre silniki wyrażeń regularnych (takie jak Perl) są w pełni Turinga. To prawda, że ​​nawet te są słabymi narzędziami do analizowania HTML, ale ten często cytowany argument nie jest powodem.
dubiousjim
4

Myślę, że problemy sprowadzają się do:

  1. Wyrażenie regularne jest prawie zawsze niepoprawne. Istnieją uzasadnione dane wejściowe, których nie uda się poprawnie dopasować. Jeśli pracujesz wystarczająco ciężko, możesz sprawić, by było w 99% poprawne lub 99,999%, ale uczynienie go w 100% poprawnym jest prawie niemożliwe, choćby z powodu dziwnych rzeczy, na które XML pozwala na używanie jednostek.

  2. Jeśli wyrażenie regularne jest niepoprawne, nawet dla 0,00001% danych wejściowych, masz problem z bezpieczeństwem, ponieważ ktoś może odkryć dane wejściowe, które spowodują uszkodzenie aplikacji.

  3. Jeśli wyrażenie regularne jest na tyle poprawne, że obejmuje 99,99% przypadków, będzie całkowicie nieczytelne i niemożliwe do utrzymania.

  4. Jest bardzo prawdopodobne, że wyrażenie regularne będzie bardzo źle działać na plikach wejściowych średniej wielkości. Moje pierwsze spotkanie z XML polegało na zastąpieniu skryptu Perl, który (niepoprawnie) analizował przychodzące dokumenty XML odpowiednim parserem XML, i nie tylko zastąpiliśmy 300 linii nieczytelnego kodu 100 liniami, które każdy mógł zrozumieć, ale poprawiliśmy czas reakcji użytkownika od 10 sekund do około 0,1 sekundy.

Michael Kay
źródło
1

Ogólnie mówiąc, XML nie może być analizowany przy użyciu wyrażenia regularnego, ponieważ gramatyka XML nie jest bynajmniej regularna. Krótko mówiąc, wyrażenia regularne nie mogą się liczyć (no cóż, wyrażenia regularne Perla mogą być w stanie policzyć rzeczy), więc nie można zrównoważyć tagów otwieranie-zamykanie.

Nie zgadzam się. Jeśli użyjesz rekurencyjnego w wyrażeniach regularnych, możesz łatwo znaleźć otwieranie i zamykanie tagów.

Tutaj pokazałem przykład wyrażenia regularnego, aby uniknąć błędów parsowania przykładów w pierwszej wiadomości.

Maxim Susłow
źródło
Po pierwsze, rekursywne wyrażenia regularne nie są wyrażeniami regularnymi (jeśli spojrzysz w nawiasach, zobaczysz, że przyznaję, że wyrażenia regularne Perla, które są rekurencyjne, mogą liczyć rzeczy, które są wymagane do obsługi HTML). Po drugie, twój przykład dotyczy dobrze sformatowanego XHTML lub XML. HTML nie jest dobrze sformułowany. Po trzecie, musisz zadać sobie pytanie, czy łatwiej jest rozszerzyć i utrzymywać analizator składni napisany w rekurencyjnym języku regularnym lub w języku programowania ogólnego przeznaczenia.
Chas. Owens,
Po czwarte, nawet twój przykład jest trywialnie zepsuty, mimo że nadal jest poprawnym XML. Dodaj jedną spację między blokiem treści a identyfikatorem, a to się nie powiedzie. Jestem pewien, że gdybym poświęcił jeszcze kilka minut, znalazłbym inny błąd strukturalny w twoim kodzie. To po prostu nie jest dobry pomysł.
Chas. Owens,
1

Dałem uproszczoną odpowiedź na ten problem tutaj . Chociaż nie uwzględnia znaku 100%, wyjaśniam, jak to możliwe, jeśli chcesz wykonać pewne czynności związane z przetwarzaniem wstępnym.

Erutan409
źródło