Puoi fornire alcuni esempi del perché è difficile analizzare XML e HTML con una regex? [chiuso]


402

Un errore che vedo gente che fa su e più volte sta cercando di analizzare XML o HTML con una regex. Ecco alcuni dei motivi per cui l'analisi di XML e HTML è difficile:

Le persone vogliono trattare un file come una sequenza di linee, ma questo è valido:

<tag
attr="5"
/>

Le persone vogliono considerare <o <tag come l'inizio di un tag, ma roba del genere esiste in natura:

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

Le persone spesso vogliono abbinare i tag di partenza ai tag di fine, ma XML e HTML consentono ai tag di contenere se stessi (che i regex tradizionali non possono gestire affatto):

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

Le persone spesso vogliono confrontarsi con il contenuto di un documento (come il famoso problema "trova tutti i numeri di telefono su una determinata pagina"), ma i dati potrebbero essere contrassegnati (anche se sembrano normali quando visualizzati):

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

I commenti possono contenere tag scarsamente formattati o incompleti:

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

Di quali altri Gotcha sei a conoscenza?


14
I browser Web hanno senso di questo tipo di pasticcio milioni di volte al secondo, qualcuno non può creare una classe di parser per pagine Web per noi semplici mortali?
Jon Winstanley,

24
Jon, hanno. In Perl ci sono molti HTML :: Parser, HTML :: TreeBuilder, ecc. Ce n'è quasi sicuramente uno per la tua lingua.
Chas. Owens

12
La risposta migliore è, stackoverflow.com/a/1732454/135078 (attenzione Zalgo)
Kelly S. francese

3
C'è una buona spiegazione del perché [non puoi analizzare [X] HTML con regex] [1] [1]: stackoverflow.com/a/1732454/468725
Pavel P

4
Ecco una buona spiegazione di come puoi sicuramente analizzare l'HTML con i pattern , e anche perché probabilmente non desideri farlo.
tchrist,

Risposte:


260

Ecco alcuni divertenti XML validi per te:

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

E questo piccolo fascio di gioia è un HTML valido:

<!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>

Per non parlare di tutte le analisi specifiche del browser per costrutti non validi.

Buona fortuna mettendo contro regex contro quello!

EDIT (Jörg W Mittag): Ecco un altro bel pezzo di HTML 4.01 ben formato e valido:

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

6
Quello XML? Ci sono alcuni costrutti diversi lì, il che è problematico? Il sottoinsieme interno DTD? Questo sta definendo una nuova & entità; chiamato 'y', contenente una sequenza ']>' che normalmente, se non tra virgolette, terminerebbe il sottoinsieme interno.
Bobince,

16
(Ciò dimostra che devi avere una conoscenza abbastanza profonda di alcune delle funzionalità DTD più esoteriche e arcaiche di XML per analizzare correttamente un documento, anche se non sei un parser con convalida DTD.)
bobince

17
Gli esempi HTML fanno uso di una caratteristica raramente nota: le scorciatoie. Maggiori informazioni su w3.org/QA/2007/10/shorttags.html
netvope

25
Ogni volta che qualcuno scrive HTML come mostrato sopra, Tim Berners-Lee versa una sola lacrima.
Fgysin ripristina Monica il

5
Adoro il modo in cui l'evidenziatore Syntax di Stackoverflow fallisca alla prima occorrenza di "]".
GlassGhost

71

In realtà

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

non è un HTML valido e non è neanche un XML valido.

Non è un XML valido perché "<" e ">" non sono caratteri validi all'interno delle stringhe di attributi. Devono essere salvati utilizzando le entità XML corrispondenti & lt; e & gt;

Non è un codice HTML valido neanche perché il modulo di chiusura breve non è consentito in HTML (ma è corretto in XML e XHTML). Il tag 'img' è anche un tag implicitamente chiuso secondo la specifica HTML 4.01. Ciò significa che chiuderlo manualmente è in realtà errato ed equivale a chiudere qualsiasi altro tag due volte.

La versione corretta in HTML è

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

e la versione corretta in XHTML e XML è

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

Anche il seguente esempio che hai fornito non è valido

<
tag
attr="5"
/>

Questo non è neanche HTML o XML valido. Il nome del tag deve essere proprio dietro '<', sebbene gli attributi e la chiusura '>' possano essere dove vogliono. Quindi l'XML valido è in realtà

<tag
attr="5"
/>

Ed eccone un altro più funkier: puoi effettivamente scegliere di usare "o" come carattere di citazione dell'attributo

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

Tutti gli altri motivi che sono stati pubblicati sono corretti, ma il problema più grande con l'analisi dell'HTML è che le persone di solito non capiscono correttamente tutte le regole di sintassi. Il fatto che il tuo browser interpreti il ​​tuo tagsoup come HTML non significa che tu abbia effettivamente scritto HTML valido.

Modifica: E anche stackoverflow.com è d'accordo con me per quanto riguarda la definizione di valido e non valido. Il tuo XML / HTML non valido non è evidenziato, mentre la mia versione corretta è.

Fondamentalmente, XML non è fatto per essere analizzato con regexps. Ma non c'è nemmeno motivo di farlo. Esistono molti, molti parser XML per ogni lingua. È possibile scegliere tra parser SAX, parser DOM e pull parser. Tutti questi sono garantiti per essere molto più veloci rispetto all'analisi con una regexp e quindi è possibile utilizzare tecnologie interessanti come XPath o XSLT sull'albero DOM risultante.

La mia risposta è quindi: non solo è difficile analizzare XML con regexps, ma è anche una cattiva idea. Basta usare uno dei milioni di parser XML esistenti e sfruttare tutte le funzionalità avanzate di XML.

L'HTML è troppo difficile anche solo per provare ad analizzare da solo. Innanzitutto la sintassi legale ha molte piccole sottigliezze di cui potresti non essere a conoscenza, e in secondo luogo, l'HTML in natura è solo un enorme mucchio puzzolente di (ottieni la mia deriva). Esistono diverse librerie di parser lassisti che fanno un buon lavoro nella gestione di HTML come tag soup, basta usarle.


8
Non è necessario scappare> come> però.
Joey,

8
Ok, s / valid / esiste in natura / g
Chas. Owens

1
In realtà, in base alle specifiche devi scappare> as> così come devi scappare <as <& and & amp; e negli attributi "as & quot; e 'as & apos; è solo quel parser
LordOfThePigs

19
La specifica non dice '>' deve essere evitato - tranne il caso speciale della sequenza ']]>' nel contenuto. Per questo motivo è più facile sfuggire sempre a '>', ma non è richiesto dalle specifiche.
Bobince,

8
>il segno è perfettamente valido in html stackoverflow.com/questions/94528/…
jfs

56

Ho scritto un intero post sul blog su questo argomento: Limitazioni delle espressioni regolari

Il punto cruciale del problema è che HTML e XML sono strutture ricorsive che richiedono meccanismi di conteggio per poter analizzare correttamente. Un vero regex non è in grado di contare. Per contare, devi avere una grammatica libera dal contesto.

Il paragrafo precedente presenta un leggero avvertimento. Alcune implementazioni regex ora supportano l'idea della ricorsione. Tuttavia, una volta che inizi ad aggiungere la ricorsione nelle tue espressioni regex, stai davvero allungando i confini e dovresti considerare un parser.


20

Un gotcha non presente nel tuo elenco è che gli attributi possono apparire in qualsiasi ordine, quindi se il tuo regex è alla ricerca di un collegamento con l'href "pippo" e la classe "bar", possono venire in qualsiasi ordine e avere un numero qualsiasi di altri cose tra loro.


Ah, sì, quella era persino la domanda che mi ha spinto a porre questo (il primo link).
Chas. Owens

16

Dipende da cosa intendi per "analisi". In generale, XML non può essere analizzato usando regex poiché la grammatica XML non è affatto regolare. Per dirla semplicemente, le regex non possono contare (beh, le regex del Perl potrebbero effettivamente essere in grado di contare le cose), quindi non è possibile bilanciare i tag open-close.


immagino che i backreferences possano risolvere il problema dei tag di apertura e chiusura
Rishul Matta,

1
@RishulMatta: come? Hai solo un numero limitato di backreferences e nota che devi invertire i tag ... Inoltre la rigida definizione di regex non consente backreferences.
Willem Van Onsem,

.NET consente di bilanciare espressioni, che pop e push, e potrebbero teoricamente essere usate per abbinare la gerarchia. Ma è ancora una cattiva idea.
Abele,

9

Le persone stanno effettivamente commettendo un errore usando una regex o è semplicemente abbastanza buona per il compito che stanno cercando di raggiungere?

Sono totalmente d'accordo sul fatto che l'analisi di html e xml utilizzando una regex non sia possibile come hanno risposto altre persone.

Tuttavia, se il tuo requisito non è di analizzare html / xml ma di ottenere solo un piccolo bit di dati in un bit "noto" di html / xml, forse un'espressione regolare o anche una "sottostringa" ancora più semplice è abbastanza buona.


7
Definisci "abbastanza buono". Inevitabilmente il semplice regex non funzionerà. Non è abbinare qualcosa o abbinare qualcosa che non dovresti avere un bug? In tal caso, l'utilizzo di regex è un errore. I parser HTML e XML non sono difficili da usare. Evitare di impararli è una falsa economia.
Chas. Owens,

1
ok, definisci "abbastanza buono". Diciamo che ho una pagina web che mi dice l'indirizzo IP dei client. Questo è tutto. Ora, devo scrivere un'applicazione per la macchina client che mi dice il suo indirizzo IP. Vado a quel sito, cerco un indirizzo IP e lo restituisco. L'analisi dell'HTML non è necessaria!
Robin Day

2
Se hai una stringa arbitraria il cui formato è completamente sotto il tuo controllo, il fatto che la stringa sia in formato XML ben formato non è rilevante. Ma quasi nessun caso d'uso per XML rientra effettivamente in questa categoria.
Robert Rossney,

15
Posso dirti per esperienza dolorosa che la maggior parte delle volte è possibile ottenere ciò che vuoi utilizzando schemi di regex complessi assurdi. Fino a quando il sito web subisce un piccolo cambiamento esilarante e puoi lanciare questa regex che ti ha fatto piangere per due giorni fuori dalla finestra e ricominciare da capo.
Thomasz,

@Robert: "quasi nessun caso d'uso" è un'esagerazione. Nella mia esperienza ci sono casi d'uso abbastanza comuni. YAGNI si applica qui ... a volte. Il trucco è sapere quanto la tua soluzione deve essere a prova di proiettile e di lunga durata, per il compito specifico che stai affrontando. Robin ha un buon punto. Sta solo dicendo che l'analisi del XML completo non ne vale sempre la pena ... il che è vero anche se sai come usarlo.
LarsH

6

Le persone normalmente non scrivono modelli avidi, spesso abbastanza da portare a un non ponderato. * Slurping grandi blocchi di file nel più grande <foo>. * </foo>.


2
Oltre a rendere pigra la ripetizione .*?<, puoi risolverlo usando una classe di caratteri negata come [^<]*<. (Dichiarazione di non responsabilità: ovviamente non è ancora infallibile, questo è il punto della domanda.)
Rory O'Kane

6

Sono tentato di dire "non reinventare la ruota". Solo che XML è un formato davvero molto complesso. Quindi forse dovrei dire "non reinventare il sincrotrone".

Forse il cliché corretto inizia "quando tutto ciò che hai è un martello ..." Sai usare le espressioni regolari, le espressioni regolari sono brave ad analizzare, quindi perché preoccuparsi di imparare una libreria di analisi XML?

Perché analizzare XML è difficile . Ogni sforzo che risparmi non dovendo imparare a usare una libreria di analisi XML sarà più che compensato dalla quantità di lavoro creativo e dallo scambio di bug che dovrai fare. Per il tuo bene, google "libreria XML" e sfrutta il lavoro di qualcun altro.


3
Non è complesso come C ++ però.
Cole Johnson,

6
@Cole "Cole9" Johnson Non userei nemmeno RE per analizzare C ++.
Isaac Rabinovitch il

2
Se XML è un sincrotrone, C ++ sarebbe il Large Hadron Collider.
Kevin Kostlan,

4

Credo che questo classico abbia le informazioni che stai cercando. Puoi trovare il punto in uno dei commenti lì:

Penso che il difetto qui sia che HTML è una grammatica di Chomsky Type 2 (grammatica senza contesto) e RegEx è una grammatica di Chomsky Type 3 (espressione regolare). Poiché una grammatica di tipo 2 è fondamentalmente più complessa di una grammatica di tipo 3, non puoi sperare di farlo funzionare . Ma molti ci proveranno, alcuni rivendicheranno il successo e altri troveranno la colpa e ti rovineranno totalmente.

Altre informazioni da Wikipedia: Chomsky Hierarchy


6
"Espressione regolare" non ha esattamente lo stesso significato nelle discussioni grammaticali formali come qui. La maggior parte dei motori regex esistenti sono più potenti delle grammatiche Chomsky di tipo 3 (ad es. Abbinamento non avido, backref). Alcuni motori regex (come quelli di Perl) sono Turing completi. È vero che anche quelli sono strumenti scadenti per l'analisi dell'HTML, ma questo argomento spesso citato non è il motivo.
dubiousjim

4

Penso che i problemi si riducano a:

  1. La regex è quasi invariabilmente errata. Ci sono input legittimi che non riusciranno a trovare correttamente. Se lavori abbastanza duramente puoi renderlo corretto al 99%, o al 99,999%, ma renderlo corretto al 100% è quasi impossibile, se non altro per le strane cose che XML consente usando le entità.

  2. Se la regex non è corretta, anche per lo 0,00001% degli input, allora hai un problema di sicurezza, perché qualcuno può scoprire l'unico input che romperà la tua applicazione.

  3. Se il regex è abbastanza corretto da coprire il 99,99% dei casi, sarà completamente illeggibile e non mantenibile.

  4. È molto probabile che un regex funzionerà molto male su file di input di dimensioni moderate. Il mio primo incontro con XML è stato quello di sostituire uno script Perl che analizzava (erroneamente) i documenti XML in arrivo con un parser XML appropriato e non solo abbiamo sostituito 300 righe di codice illeggibile con 100 righe che chiunque poteva capire, ma abbiamo migliorato i tempi di risposta degli utenti da 10 secondi a circa 0,1 secondi.


1

In generale, XML non può essere analizzato usando regex poiché la grammatica XML non è affatto regolare. Per dirla semplicemente, le regex non possono contare (beh, le regex del Perl potrebbero effettivamente essere in grado di contare le cose), quindi non è possibile bilanciare i tag open-close.

Non sono d'accordo. Se utilizzerai ricorsivo in regex, puoi facilmente trovare tag di apertura e chiusura.

Qui ho mostrato esempi di regex per evitare di analizzare errori di esempi nel primo messaggio.


In primo luogo, le regex ricorsive non sono espressioni regolari (se guardi tra parentesi, vedrai che concedo che le regex di Perl, che sono ricorsive, possono contare le cose, necessarie per gestire l'HTML). In secondo luogo, il tuo esempio è per XHTML o XML che è ben formato. HTML non è ben formato. Terzo, devi chiederti: è più facile estendere e mantenere un parser scritto in un linguaggio regex ricorsivo o in un linguaggio di programmazione generico.
Chas. Owens

In quarto luogo, anche il tuo esempio è banalmente rotto pur essendo XML valido. Aggiungi uno spazio tra content_block e id e non riesce. Sono certo che se impiegassi qualche minuto in più troverei qualche altro errore strutturale nel tuo codice. Non è una buona idea.
Chas. Owens

1

Ho dato una risposta semplificata a questo problema qui . Sebbene non rappresenti il ​​segno del 100%, spiego come è possibile se si è disposti a fare un lavoro di pre-elaborazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.