RegEx corrisponde ai tag aperti ad eccezione dei tag autonomi XHTML


1474

Devo abbinare tutti questi tag di apertura:

<p>
<a href="foo">

Ma non questi:

<br />
<hr class="foo" />

Ho pensato a questo e volevo assicurarmi di averlo fatto bene. Sto solo catturando il a-z.

<([a-z]+) *[^/]*?>

Credo che dice:

  • Trova un meno di, quindi
  • Trova (e acquisisci) az una o più volte, quindi
  • Trova zero o più spazi, quindi
  • Trova qualsiasi personaggio zero o più volte, avido, tranne /, quindi
  • Trova un maggiore di

Ho ragione? E, soprattutto, cosa ne pensi?

Risposte:


4417

Non puoi analizzare [X] HTML con regex. Perché HTML non può essere analizzato da regex. Regex non è uno strumento che può essere utilizzato per analizzare correttamente HTML. Come ho risposto alle domande HTML-e-regex qui così tante volte prima, l'uso di regex non ti permetterà di consumare HTML. Le espressioni regolari sono uno strumento insufficientemente sofisticato per comprendere i costrutti impiegati dall'HTML. L'HTML non è un linguaggio normale e quindi non può essere analizzato dalle espressioni regolari. Le query Regex non sono attrezzate per suddividere l'HTML nelle sue parti significative. così tante volte ma non mi sta arrivando. Anche le espressioni regolari irregolari potenziate utilizzate da Perl non sono all'altezza dell'analisi dell'HTML. Non mi farai mai scoppiare. HTML è un linguaggio di complessità sufficiente che non può essere analizzato da espressioni regolari. Perfino Jon Skeet non può analizzare HTML usando espressioni regolari. Ogni volta che tenti di analizzare l'HTML con espressioni regolari, il bambino empio piange il sangue delle vergini e gli hacker russi pubblicano la tua webapp. Analizzare HTML con regex evoca anime contaminate nel regno dei vivi. HTML e regex vanno insieme come l'amore, il matrimonio e l'infanticidio rituale. Il <center> non può trattenerlo è troppo tardi. La forza di regex e HTML insieme nello stesso spazio concettuale distruggerà la tua mente come uno stucco acquoso. Se analizzi HTML con regex, stai cedendo a Loro e ai loro modi blasfemi che ci condannano a disumano lavoro per Colui il cui Nome non può essere espresso nel Piano Multilingue di Base, viene. HTML-plus-regexp liquefà i nervi dei senzienti mentre osservi, la tua psiche appassire nell'assalto dell'orrore.è troppo tardi è troppo tardi non possiamo essere salvati dal trangession di un chi ensuresld assicura che regex consumerà tutto il tessuto vivente (tranne l'HTML che non può, come precedentemente profetizzato) caro signore aiutarci come si può sopravvivere a questo flagello usando regex per analizzare L'HTML ha condannato l'umanità a un'eternità di terribili torture e falle nella sicurezza usando rege x come strumento per elaborare l'HTML stabilisce una breccia tra questo mondo e il terrore regno delle entità in crisi (come entità SGML, ma più corrotte) un semplice assaggio di il mondo del reg ex parser per HTML sarà ins tantly trasporto ap coscienza di rogrammer i nto aw ORL d di incessante urla, viene, Il pestifero sl ithy regex-infezione wil l divorare il tuo HT parser ML, l'applicazione e l'esistenza di tutti i tempi come Visual Basic solo peggio arriva lui com es non fi ght h E è, hi s Unholy Radiance de stro҉ying tutto l'illuminazione, i tag HTML che perde fr̶ǫm yo ur occhi come liq uid p ain, il canto di regolare exp ri analisi fissione si EXTI nguish le voci di Mor uomo Tal, dalla sp qui posso vedere che si può vedere E 'è bello t ha f inal snuffing o f la menzogna s di uomo tutto è perduto Un LL I SLOST ° e pony è venuto s ha com es ha co me s t ha ich o permeato es al L Il mio FAC E MIA FACCIA ᵒh dio n o NO Noo O SU Θ arresto t ha un * ̶͑̾̾ GL ES ͎a̧͈͖r̽̾̈́͒͑en ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


Hai provato a usare un parser XML invece?


Nota del moderatore

Questo post è bloccato per impedire modifiche inappropriate al suo contenuto. Il post appare esattamente come dovrebbe essere - non ci sono problemi con il suo contenuto. Si prega di non contrassegnarlo per la nostra attenzione.


179
Kobi: Penso che sia tempo per me di lasciare il posto di Assistant Don't Parse HTML With Regex Officer. Non importa quante volte lo diciamo, non smetteranno di venire ogni giorno ... ogni ora anche. È una causa persa, che qualcun altro può combattere per un po '. Quindi continua, analizza HTML con regex, se necessario. È solo codice infranto, non vita o morte.
Bobince,

27
È possibile utilizzare RegEx per analizzare questa risposta?
Chris Porter,

2
Se non riesci a vedere questo post, eccone uno screencapture in tutto il suo splendore: imgur.com/gOPS2.png
Andrew Keeton,

3251

Sebbene un HTML arbitrario con solo una regex sia impossibile, a volte è appropriato usarli per analizzare un insieme limitato e noto di HTML.

Se si dispone di un piccolo set di pagine HTML da cui si desidera acquisire dati e quindi inserirli in un database, le regex potrebbero funzionare correttamente. Ad esempio, recentemente ho voluto ottenere i nomi, i partiti e i distretti dei rappresentanti federali australiani, che ho ottenuto dal sito web del Parlamento. Questo era un lavoro limitato, una tantum.

Regexes ha funzionato bene per me ed è stato molto veloce da configurare.


131
Inoltre, raschiare dati formattati in modo abbastanza regolare da documenti di grandi dimensioni sarà VELOCEMENTE più veloce con un uso giudizioso di scan & regex rispetto a qualsiasi parser generico. E se hai dimestichezza con le regex di codifica, codifica molto più velocemente rispetto alla codifica di xpaths. E quasi sicuramente meno fragile ai cambiamenti in ciò che stai raschiando. Quindi bleh.
Michael Johnston,

255
@MichaelJohnston "Meno fragile"? Quasi certamente no. Si preoccupa dei dettagli di formattazione del testo che un parser XML può ignorare silenziosamente. Passare tra &foo;codifiche e CDATAsezioni? Utilizzando un minificatore HTML per rimuovere tutti gli spazi bianchi nel documento che il browser non visualizza? A un parser XML non importerà, e nemmeno una dichiarazione XPath ben scritta. Un "parser" basato su regex, d'altra parte ...
Charles Duffy,

41
@CharlesDuffy per un lavoro una tantum va bene, e per gli spazi che usiamo \ s +
quantum

68
@xiaomao in effetti, se dovessi conoscere tutti i trucchi e le soluzioni alternative per ottenere una soluzione all'80% che fallisce il resto del tempo "funziona per te", non posso fermarti. Nel frattempo, sono finito dalla mia parte del recinto usando parser che funzionano al 100% di XML sintatticamente valido.
Charles Duffy,

374
Una volta ho dovuto estrarre alcuni dati da ~ 10k pagine, tutte con lo stesso modello HTML. Sono stati disseminati di errori HTML che hanno causato il soffocamento dei parser e tutto il loro stile era in linea o con <font>ecc .: nessuna classe o ID per aiutare a navigare nel DOM. Dopo aver combattuto tutto il giorno con l'approccio "giusto", sono finalmente passato a una soluzione regex e l'ho fatto funzionare in un'ora.
Paul A Jungwirth,

2039

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 (grammatica regolare) . Poiché una grammatica di tipo 2 è fondamentalmente più complessa di una grammatica di tipo 3 (vedere la gerarchia di Chomsky ), è matematicamente impossibile analizzare XML con RegEx.

Ma molti ci proveranno, alcuni addirittura rivendicheranno il successo, ma fino a quando altri non troveranno l'errore e ti sbaglieranno completamente.


226
L'OP chiede di analizzare un sottoinsieme molto limitato di XHTML: start tags. Ciò che rende (X) HTML un CFG è il suo potenziale di avere elementi tra i tag di inizio e fine di altri elementi (come in una regola grammaticale A -> s A e). (X) HTML non ha questa proprietà all'interno di un tag iniziale: un tag iniziale non può contenere altri tag iniziali. Il sottoinsieme che l'OP sta cercando di analizzare non è un CFG.
LarsH

101
Nella teoria CS, i linguaggi regolari sono un sottoinsieme rigoroso di linguaggi senza contesto, ma le implementazioni di espressioni regolari nei linguaggi di programmazione tradizionali sono più potenti. Come descrive noulakaz.net/weblog/2007/03/18/… , le cosiddette "espressioni regolari" possono verificare la presenza di numeri primi in unario, il che è certamente qualcosa che un'espressione regolare della teoria CS non può realizzare.
Adam Mihalcin,

11
@eyelidlessness: lo stesso "solo se" si applica a tutti i CFG, no? Vale a dire se l'input HTML (X) non è ben formato, nemmeno un parser XML completo funzionerà in modo affidabile. Forse se fornisci esempi degli "errori di sintassi HTML (X) implementati negli agenti utente del mondo reale" a cui ti riferisci, capirò cosa stai migliorando.
LarsH,

82
@AdamMihalcin ha esattamente ragione. La maggior parte dei motori regex esistenti sono più potenti delle grammatiche di 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

27
Questa è la risposta più "completa e breve" qui. Porta le persone ad apprendere le basi di grammatiche e lingue formali e, auspicabilmente, alcuni calcoli matematici in modo da non perdere tempo in cose senza speranza come risolvere compiti NP in tempi polinomiali
mishmashru,

1332

Non ascoltare questi ragazzi. È del tutto possibile analizzare grammatiche context-free con regex se si interrompe il compito in pezzi più piccoli. È possibile generare il modello corretto con uno script che esegua ciascuno di questi nell'ordine:

  1. Risolvi il problema dell'arresto.
  2. Piazza un cerchio.
  3. Risolvi il problema del commesso viaggiatore in O (registro n) o meno. Se è qualcosa di più, la RAM si esaurisce e il motore si bloccherà.
  4. Il modello sarà piuttosto grande, quindi assicurati di avere un algoritmo che comprime senza perdita di dati casuali.
  5. Quasi lì: basta dividere il tutto per zero. Vai tranquillo.

Non ho ancora finito l'ultima parte da solo, ma so che mi sto avvicinando. Continua a lanciare CthulhuRlyehWgahnaglFhtagnExceptions per qualche motivo, quindi ho intenzione di portarlo su VB 6 e usarlo On Error Resume Next. Aggiornerò con il codice una volta indagato su questa strana porta che si è appena aperta sul muro. Hmm.

PS Pierre de Fermat ha anche capito come farlo, ma il margine in cui stava scrivendo non era abbastanza grande per il codice.


80
La divisione per zero è un problema molto più semplice rispetto agli altri di cui parli. Se usi gli intervalli, piuttosto che l'aritmetica in virgola mobile (che tutti dovrebbero essere ma nessuno lo è), puoi dividere felicemente qualcosa per [un intervallo contenente] zero. Il risultato è semplicemente un intervallo contenente più e meno infinito.
rjmunro,

148
Il problema del piccolo margine di Fermat è stato risolto dai margini deboli nei moderni software di modifica del testo.
kd4ttc

50
Il problema del piccolo margine di Fermat è stato risolto da Randall Munroe impostando il carattere a zero: xkcd.com/1381
heltonbiker

29
Cordiali saluti: Il problema di Fermat è stato effettivamente risolto nel 1995 e ci sono voluti solo 358 anni per i matematici.
jmiserez,

10
Sono stato in grado di aggirare questo appiccicoso passo diviso per zero usando invece i cricchetti browniani ottenuti dalla fusione fredda ... sebbene funzioni solo quando rimuovo la costante cosmologica.
Tim Lehner,

1073

Dichiarazione di non responsabilità : utilizzare un parser se si dispone dell'opzione. Detto ciò...

Questa è la regex che uso (!) Per abbinare i tag HTML:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

Potrebbe non essere perfetto, ma ho eseguito questo codice attraverso un sacco di HTML. Nota che cattura anche cose strane come <a name="badgenerator"">, che appaiono sul web.

Immagino che non corrisponda ai tag autonomi, o vorresti usare il look-behind negativo di Kobi :

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>

o semplicemente combinare se e se no.

Per i downvoter: questo è il codice funzionante di un prodotto reale. Dubito che chiunque legga questa pagina avrà l'impressione che sia socialmente accettabile usare regex su HTML.

Avvertenza : dovrei notare che questa regex si interrompe ancora in presenza di blocchi CDATA, commenti, elementi di script e di stile. La buona notizia è che puoi sbarazzarti di quelli che usano una regex ...


95
Vorrei andare con qualcosa che funzioni su cose sane piuttosto che piangere per non essere universalmente perfetto :-)
prajeesh kumar

55
Qualcuno sta usando CDATA all'interno di HTML?
Danubian Sailor

16
quindi in realtà non risolvi il problema di analisi solo con regexp ma come parte del parser potrebbe funzionare. PS: prodotto funzionante non significa buon codice. Senza offesa, ma è così che funziona la programmazione industriale e ottiene i loro soldi
mishmashru,

32
L'avvio del regex non riescono proprio il più breve possibile, HTML valido: <!doctype html><title><</title>. '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)Restituisce semplici ["<!doctype html>", "<title>", "<</title>"]mentre dovrebbe ["<title>", "</title>"].

2
se stiamo solo cercando di abbinare e non abbinare gli esempi forniti, /<.([^r>[[^>×*)?>/g funziona :-) // javascript: '<p> <a href = "foo"> <br /> <hr class = "foo" />'.match
/<.([^r>

506

Ci sono persone che ti diranno che la Terra è rotonda (o forse che la Terra è uno sferoide oblato se vogliono usare parole strane). Stanno mentendo.

Ci sono persone che ti diranno che le espressioni regolari non dovrebbero essere ricorsive. Ti stanno limitando. Hanno bisogno di soggiogarti e lo fanno tenendoti nell'ignoranza.

Puoi vivere nella loro realtà o prendere la pillola rossa.

Come Lord Marshal (è lui un parente della classe maresciallo NET?), Ho visto l'Underverse Stack Based Regex-Verse e tornò con poteri conoscenza non si può immaginare. Sì, penso che ci fossero un Vecchio o due a proteggerli, ma stavano guardando il calcio in TV, quindi non è stato difficile.

Penso che il caso XML sia abbastanza semplice. Il RegEx (nella sintassi .NET), sgonfiato e codificato in base64 per facilitare la comprensione da parte della tua mente debole, dovrebbe essere qualcosa del genere:

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
+r/vD34mUADO1P4/AQAA//8=

Le opzioni da impostare sono RegexOptions.ExplicitCapture. Il gruppo di acquisizione che stai cercando è ELEMENTNAME. Se il gruppo di acquisizione ERRORnon è vuoto, si è verificato un errore di analisi e Regex si è interrotto.

Se hai problemi a riconvertirlo in una regex leggibile dall'uomo, questo dovrebbe aiutare:

static string FromBase64(string str)
{
    byte[] byteArray = Convert.FromBase64String(str);

    using (var msIn = new MemoryStream(byteArray))
    using (var msOut = new MemoryStream()) {
        using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
            ds.CopyTo(msOut);
        }

        return Encoding.UTF8.GetString(msOut.ToArray());
    }
}

Se non sei sicuro, no, NON sto scherzando (ma forse sto mentendo). Funzionerà. Ho costruito tonnellate di test unitari per testarlo e ho persino usato (parte di) test di conformità . È un tokenizer, non un parser completo, quindi dividerà solo l'XML nei suoi token componenti. Non analizzerà / integrerà i DTD.

Oh ... se vuoi il codice sorgente del regex, con alcuni metodi ausiliari:

regex per tokenizzare un xml o la regex normale completa


68
Buon Dio, è enorme. La mia più grande domanda è perché? Ti rendi conto che tutti i linguaggi moderni hanno parser XML, giusto? Puoi fare tutto questo come in 3 righe ed essere sicuro che funzionerà. Inoltre, si fa anche conto che pura espressione regolare è dimostrabilmente in grado di fare certe cose? A meno che tu non abbia creato un parser ibrido regex / codice imperativo, ma non sembra che tu abbia. Riesci a comprimere anche dati casuali?
Justin Morgan,

113
@Justin Non ho bisogno di un motivo. Potrebbe essere fatto (e non era illegale / immorale), quindi l'ho fatto. Non ci sono limiti alla mente tranne quelli che riconosciamo (Napoleon Hill) ... I linguaggi moderni possono analizzare XML? Veramente? E ho pensato che fosse illegale! :-)
xanatos

76
Signore, ne sono convinto. Userò questo codice come parte del kernel per la mia macchina a moto perpetuo - puoi credere che quei pazzi dell'ufficio brevetti continuino a rifiutare la mia domanda? Bene, li mostrerò. Li mostrerò tutti!
Justin Morgan,

31
@Justin Quindi un parser Xml è per definizione privo di bug, mentre un Regex non lo è? Perché se un parser Xml non è privo di bug per definizione, potrebbe esserci un XML che lo fa andare in crash e torniamo al passaggio 0. Diciamo questo: sia il parser Xml che questo Regex cercano di essere in grado di analizzare tutto il "legale "XML. POSSONO analizzare un XML "illegale". I bug potrebbero mandare in crash entrambi. C # XmlReader è sicuramente più testato di questo Regex.
Xanatos,

31
No, niente è privo di bug: 1) Tutti i programmi contengono almeno un bug. 2) Tutti i programmi contengono almeno una riga di codice sorgente non necessario. 3) Con # 1 e # 2 e usando l'induzione logica, è semplice provare che qualsiasi programma può essere ridotto a una singola riga di codice con un bug. (da Learning Perl)
Scott Weaver,

299

Nella shell, puoi analizzare HTML usando sed :

  1. Turing.sed
  2. Scrivi parser HTML (compiti a casa)
  3. ???
  4. Profitto!

Correlati (perché non dovresti usare la corrispondenza regex):


3
Temo che non hai capito la battuta, @kenorb. Per favore, leggi di nuovo la domanda e la risposta accettata. Non si tratta di strumenti di analisi HTML in generale, né di strumenti di analisi HTML shell, si tratta di analizzare HTML tramite regex.
Palec,

1
No, @Abdul. È completamente, dimostrabilmente (in senso matematico) impossibile.
Palec,

3
Sì, questa risposta la riassume bene, @Abdul. Si noti che, tuttavia, le implementazioni regex non sono in realtà espressioni regolari in senso matematico - hanno costrutti che le rendono più forti, spesso complete di Turing (equivalenti alle grammatiche di tipo 0). L'argomento si rompe con questo fatto, ma è ancora in qualche modo valido nel senso che le regex non sono mai state pensate per essere in grado di fare un simile lavoro, però.
Palec,

2
E a proposito, la battuta a cui mi riferivo era il contenuto di questa risposta prima delle modifiche (radicali) di kenorb, in particolare la revisione 4, @Abdul.
Palec,

3
La cosa divertente è che OP non ha mai chiesto di analizzare html usando regex. Ha chiesto di abbinare il testo (che sembra essere HTML) usando regex. Il che è perfettamente ragionevole.
Paralife,

274

Concordo sul fatto che lo strumento giusto per analizzare XML e soprattutto HTML sia un parser e non un motore di espressione regolare. Tuttavia, come altri hanno sottolineato, a volte l'uso di una regex è più veloce, più facile e fa il lavoro se si conosce il formato dei dati.

Microsoft ha in realtà una sezione di Best practice per le espressioni regolari in .NET Framework e parla in particolare di Considerare [ing] l'origine di input .

Le espressioni regolari hanno dei limiti, ma hai considerato quanto segue?

Il framework .NET è unico quando si tratta di espressioni regolari in quanto supporta le definizioni dei gruppi di bilanciamento .

Per questo motivo, credo che PUOI analizzare XML usando espressioni regolari. Si noti tuttavia che deve essere un XML valido (i browser perdonano molto l'HTML e consentono una sintassi XML errata all'interno dell'HTML ). Ciò è possibile poiché la "Definizione del gruppo di bilanciamento" consentirà al motore di espressione regolare di fungere da PDA.

Citazione dall'articolo 1 sopra citato:

Motore di espressioni regolari .NET

Come descritto sopra, i costrutti correttamente bilanciati non possono essere descritti da un'espressione regolare. Tuttavia, il motore di espressione regolare .NET fornisce alcuni costrutti che consentono il riconoscimento di costrutti bilanciati.

  • (?<group>) - inserisce il risultato acquisito nello stack di acquisizione con il gruppo di nomi.
  • (?<-group>) - fa apparire la parte più alta dell'acquisizione con il gruppo di nomi dallo stack di acquisizione.
  • (?(group)yes|no) - corrisponde alla parte sì se esiste un gruppo con il gruppo nome altrimenti non corrisponde alcuna parte.

Questi costrutti consentono a un'espressione regolare .NET di emulare un PDA limitato, essenzialmente consentendo versioni semplici delle operazioni dello stack: push, pop e empty. Le semplici operazioni sono praticamente equivalenti a incremento, decremento e confronto rispettivamente a zero. Ciò consente al motore di espressione regolare .NET di riconoscere un sottoinsieme dei linguaggi senza contesto, in particolare quelli che richiedono solo un semplice contatore. Ciò a sua volta consente alle espressioni regolari .NET non tradizionali di riconoscere singoli costrutti correttamente bilanciati.

Considera la seguente espressione regolare:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

Usa le bandiere:

  • Linea singola
  • IgnorePatternWhitespace (non necessario se si comprime regex e si rimuove tutto lo spazio bianco)
  • IgnoreCase (non necessario)

Spiegazione delle espressioni regolari (in linea)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?>                                        # atomic group / don't backtrack (faster)
   <!-- .*? -->                 |          # match xml / html comment
   <[^>]*/>                     |          # self closing tag
   (?<opentag><(?!/)[^>]*[^/]>) |          # push opening xml tag
   (?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag
   [^<>]*                                  # something between tags
)*                                         # match as many xml tags as possible
(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

Puoi provarlo su A Better .NET Regular Expression Tester .

Ho usato la fonte di esempio di:

<html>
<body>
<div>
   <br />
   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

Questo ha trovato la corrispondenza:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

anche se in realtà è uscito così:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

Infine, mi è piaciuto molto l'articolo di Jeff Atwood: Parsing Html The Cthulhu Way . Abbastanza divertente, cita la risposta a questa domanda che attualmente ha oltre 4k voti.


18
System.Textnon fa parte di C #. Fa parte di .NET.
John Saunders,

8
Nella prima riga del tuo regex ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...), tra "<ul" e "id" dovrebbe essere \s+, no \s*, a meno che tu non voglia che corrisponda a <ulid = ...;)
C0deH4cker

@ C0deH4cker Hai ragione, l'espressione dovrebbe avere \s+invece di \s*.
Sam,

4
Non che io lo capisca davvero, ma penso che il tuo regex fallisca<img src="images/pic.jpg" />
Scheintod

3
@Scheintod Grazie per il commento. Ho aggiornato il codice. L'espressione precedente non è riuscita per i tag a chiusura automatica che contenevano un punto /all'interno del quale non è riuscita per il tuo <img src="images/pic.jpg" />HTML.
Sam,

258

Suggerisco di usare QueryPath per analizzare XML e HTML in PHP. È praticamente la stessa sintassi di jQuery, solo sul lato server.


8
@ Kyle: jQuery non analizza XML, ma utilizza il parser incorporato nel client (se presente). Pertanto non è necessario jQuery per farlo, ma solo due righe di semplice vecchio JavaScript . Se non è presente alcun parser incorporato, jQuery non sarà di aiuto.
RobG

1
@RobG In realtà jQuery utilizza il DOM, non il parser incorporato.
Qix - MONICA È STATA MISTREATA il

11
@ Qix — Faresti meglio a dire agli autori della documentazione quindi: " jQuery.parseXML utilizza la funzione di analisi nativa del browser ... ". Fonte: jQuery.parseXML ()
RobG

6
Essendo venuto qui dalla domanda sul meme ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), adoro che una delle risposte sia "Usa jQuery"
Jorn

221

Mentre le risposte che non puoi analizzare HTML con regex sono corrette, non si applicano qui. L'OP vuole solo analizzare un tag HTML con regex e questo è qualcosa che può essere fatto con un'espressione regolare.

La regex suggerita è sbagliata, tuttavia:

<([a-z]+) *[^/]*?>

Se aggiungi qualcosa al regex, facendo un passo indietro può essere costretto a far corrispondere cose stupide come <a >>, [^/]è troppo permissivo. Si noti inoltre che <space>*[^/]*è ridondante, perché [^/]*può anche abbinare gli spazi.

Il mio suggerimento sarebbe

<([a-z]+)[^>]*(?<!/)>

Dov'è (?<! ... )(nelle regex del Perl) lo sguardo negativo dietro. Legge "a <, quindi una parola, quindi tutto ciò che non è un>, l'ultimo dei quali potrebbe non essere un /, seguito da>".

Si noti che ciò consente cose come <a/ >(proprio come la regex originale), quindi se si desidera qualcosa di più restrittivo, è necessario creare una regex per abbinare le coppie di attributi separate da spazi.


29
+1 per notare che la domanda non riguarda l'analisi del codice HTML (X) completo, ma la corrispondenza con i tag aperti (X) HTML.
LarsH,

10
Qualcos'altro che la maggior parte delle risposte sembra ignorare, è che un parser HTML può benissimo usare espressioni regolari nella sua implementazione per parti di HTML, e sarei sorpreso se la maggior parte dei parser non lo facesse.
Thayne,

@Thayne Esattamente. Durante l'analisi dei singoli tag, un'espressione regolare è lo strumento giusto per il lavoro. È abbastanza ridicolo che si debba scorrere a metà pagina per trovare una risposta ragionevole. La risposta accettata non è corretta perché mescola lex e analisi.
Kasperd,

2
La risposta fornita qui fallirà quando un valore di attributo contiene un carattere '>' o '/'.
Martin L

Questo funzionerà in modo errato su HTML contenente commenti o sezioni CData. Inoltre non funzionerà correttamente se un attributo citato contiene un >carattere. Sono d'accordo su ciò che l'OP suggerisce può essere fatto con una regex, ma quello presentato qui è di gran lunga semplicistico.
Jacques B

183

Provare:

<([^\s]+)(\s[^>]*?)?(?<!/)>

È simile al tuo, ma l'ultimo >non deve essere dopo una barra e accetta anche h1.


107
<a href="foo" title="5> 3 "> Oops </a>
Gareth,

21
È molto vero, e ci ho pensato, ma ho pensato che il >simbolo fosse correttamente sfuggito a & gt ;.
Kobi,

65
>è valido in un valore di attributo. Infatti, nella serializzazione 'canonical XML' non è necessario utilizzare &gt;. (Il che non è del tutto rilevante, se non per sottolineare che >in un attributo il valore non è affatto una cosa insolita.)
bobince

5
@Kobi: cosa significa il segno di exlamation (quello che hai posto alla fine) in una regexp?
Marco Demaio,

6
@bobince: sei sicuro? Non capisco più, quindi è valido anche questo HTML:<div title="this tag is a <div></div>">hello</div>
Marco Demaio,

179

Sun Tzu, un antico stratega cinese, generale e filosofo, disse:

Si dice che se conosci i tuoi nemici e conosci te stesso, puoi vincere un centinaio di battaglie senza una sola perdita. Se conosci solo te stesso, ma non il tuo avversario, potresti vincere o perdere. Se non conosci né te stesso né il tuo nemico, ti metterai sempre in pericolo.

In questo caso il tuo nemico è HTML e tu sei o te stesso o regex. Potresti anche essere Perl con regex irregolare. Conosci l'HTML. Conosci te stesso.

Ho composto un haiku che descrive la natura dell'HTML.

HTML has
complexity exceeding
regular language.

Ho anche composto un haiku che descrive la natura della regex in Perl.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>

153
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) {
        var_dump( $nodeName );
    }
}

Produzione:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

Fondamentalmente basta definire i nomi dei nodi degli elementi che si chiudono da soli, caricare l'intera stringa html in una libreria DOM, prendere tutti gli elementi, scorrere e filtrare quelli che non si chiudono da soli e operare su di essi.

Sono sicuro che ormai sai già che non dovresti usare regex per questo scopo.


1
Se hai a che fare con XHTML reale, aggiungi getElementsByTagName NSe specifica lo spazio dei nomi.
meder omuraliev,

148

Non conosco la tua esatta necessità per questo, ma se stai usando anche .NET, non potresti usare Html Agility Pack ?

Estratto:

È una libreria di codici .NET che consente di analizzare i file HTML "fuori dal web". Il parser è molto tollerante con l'HTML malformato "mondo reale".


137

Vuoi che il primo >non sia preceduto da a /. Guarda qui per i dettagli su come farlo. Si chiama lookbehind negativo.

Tuttavia, un'implementazione ingenua di ciò finirà per corrispondere <bar/></foo>in questo documento di esempio

<foo><bar/></foo>

Puoi fornire qualche informazione in più sul problema che stai cercando di risolvere? Stai ripetendo i tag in modo programmatico?


1
Sì, certo che lo sono. Determinare tutti i tag attualmente aperti, quindi confrontarli con i tag chiusi in un array separato. RegEx mi fa male al cervello.
Jeff,

122

Il W3C spiega l'analisi in una forma pseudo regexp:
W3C Link

Segui i link per var QName, Se Attributeper avere un quadro più chiaro.
Sulla base di ciò puoi creare una regexp abbastanza buona per gestire cose come tag di stripping.


5
Questo non è un modulo psgeo regexp, è un modulo EBNF, come specificato qui: specifica XML, appendice 6
Rob G

106

Se ti serve questo per PHP:

Le funzioni DOM PHP non funzioneranno correttamente a meno che non sia XML formattato correttamente. Non importa quanto sia meglio il loro uso per il resto dell'umanità.

simplehtmldom è buono, ma l'ho trovato un po 'difettoso, ed è piuttosto pieno di memoria [Si bloccherà su grandi pagine.]

Non ho mai usato querypath , quindi non posso commentare la sua utilità.

Un altro da provare è il mio DOMParser che è molto leggero sulle risorse e ho usato felicemente per un po '. Semplice da imparare e potente.

Per Python e Java sono stati pubblicati collegamenti simili.

Per i downvoter - ho scritto la mia classe solo quando i parser XML si sono dimostrati incapaci di resistere all'uso reale. Il downvoting religioso impedisce solo che vengano pubblicate risposte utili - tieni le cose nella prospettiva della domanda, per favore.


95

Ecco la soluzione:

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

Per testarlo a fondo, ho inserito i tag di chiusura automatica della stringa come:

  1. <hr />
  2. <br/>
  3. <br>

Ho anche inserito i tag con:

  1. un attributo
  2. più di un attributo
  3. attributi il ​​cui valore è associato tra virgolette singole o in virgolette doppie
  4. attributi contenenti virgolette singole quando il delimitatore è una virgoletta doppia e viceversa
  5. attributi "unpretty" con uno spazio prima del simbolo "=", dopo di esso e sia prima che dopo di esso.

Se dovessi trovare qualcosa che non funziona nella dimostrazione del concetto sopra, sono disponibile ad analizzare il codice per migliorare le mie capacità.

<EDIT> Ho dimenticato che la domanda dell'utente era di evitare l'analisi dei tag a chiusura automatica. In questo caso il modello è più semplice, trasformandosi in questo:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

L'utente @ridgerunner ha notato che il modello non consente attributi non quotati o attributi senza valore . In questo caso una messa a punto ci porta il seguente schema:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</ EDIT>

Comprensione del modello

Se qualcuno è interessato a saperne di più sul modello, fornisco alcune linee:

  1. la prima sottoespressione (\ w +) corrisponde al nome del tag
  2. la seconda sottoespressione contiene il modello di un attributo. È composto da:
    1. uno o più spazi bianchi \ s +
    2. il nome dell'attributo (\ w +)
    3. zero o più spazi bianchi \ s * (è possibile o meno, lasciando spazi vuoti qui)
    4. il simbolo "="
    5. ancora, zero o più spazi bianchi
    6. il delimitatore del valore dell'attributo, una virgoletta singola o doppia ('| "). Nel modello, la virgoletta singola viene ignorata perché coincide con il delimitatore di stringa PHP. Questa sottoespressione viene catturata con le parentesi in modo che possa essere referenziata di nuovo per analizzare la chiusura dell'attributo, ecco perché è molto importante.
    7. il valore dell'attributo, corrispondente a quasi tutto: (. *?); in questa sintassi specifica, usando la partita golosa (il punto interrogativo dopo l'asterisco) il motore RegExp abilita un operatore simile a "guardare avanti", che corrisponde a tutto ciò che segue questa sottoespressione
    8. ecco che arriva il divertimento: la parte \ 4 è un operatore di backreference , che si riferisce a una sottoespressione definita precedentemente nel modello, in questo caso, mi riferisco alla quarta sottoespressione, che è il primo delimitatore di attributo trovato
    9. zero o più spazi bianchi \ s *
    10. la sottoespressione dell'attributo termina qui, con la specifica di zero o più possibili occorrenze, data dall'asterisco.
  3. Quindi, poiché un tag può terminare con uno spazio bianco prima del simbolo ">", zero o più spazi bianchi vengono abbinati al sottotatro \ s *.
  4. Il tag da abbinare può terminare con un semplice simbolo ">" o una possibile chiusura XHTML, che utilizza la barra prima di essa: (/> |>). La barra è, ovviamente, sfuggita poiché coincide con il delimitatore dell'espressione regolare.

Piccolo consiglio: per analizzare meglio questo codice è necessario guardare il codice sorgente generato poiché non ho fornito caratteri speciali HTML in escape.


12
Non corrisponde a tag validi con attributi senza valore, ad es <option selected>. Inoltre, non corrisponde a tag validi con valori di attributo non quotati, ad es <p id=10>.
ridgerunner,

1
@ridgerunner: grazie mille per il tuo commento. In tal caso il pattern deve cambiare un po ': $ pattern =' / <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\ '| "|) (. *?) \\ 5 \ s *)?) * \ S *> / '; l'ho testato e funziona in caso di attributi o attributi non quotati senza valore
Emanuele Del Grande,

Che ne dici di uno spazio prima del nome del tag: < a href="http://wtf.org" >sono abbastanza sicuro che sia legale, ma non lo abbini.
Floris,

7
NO scusa, gli spazi bianchi prima di un tagname sono illegali. Oltre ad essere "abbastanza sicuro" perché non fornisci alcune prove della tua obiezione? Ecco il mio, w3.org/TR/xml11/#sec-starttags riferito a XML 1.1, e puoi trovare lo stesso per HTML 4, 5 e XHTML, poiché una convalida W3C avviserebbe anche se fai un test. Come molti altri poeti di blah-blah da queste parti, non ho ancora ricevuto argomentazioni intelligenti, a parte alcune centinaia di meno alle mie risposte, per dimostrare dove il mio codice fallisce secondo le regole contrattuali specificate nella domanda. Li darei solo il benvenuto.
Emanuele Del Grande,

@ridgerunner ovviamente il tuo commento è stato intelligente e benvenuto.
Emanuele Del Grande,

91

Ogni volta che devo estrarre rapidamente qualcosa da un documento HTML, utilizzo Tidy per convertirlo in XML e quindi utilizzo XPath o XSLT per ottenere ciò di cui ho bisogno. Nel tuo caso, qualcosa del genere:

//p/a[@href='foo']

89

Prima ho usato uno strumento open source chiamato HTMLParser . È progettato per analizzare HTML in vari modi e serve abbastanza bene allo scopo. Può analizzare HTML come treenode diverso e puoi facilmente usare la sua API per estrarre gli attributi dal nodo. Dai un'occhiata e vedi se questo può aiutarti.


84

Mi piace analizzare HTML con espressioni regolari. Non tento di analizzare HTML idiota che è stato deliberatamente rotto. Questo codice è il mio parser principale (edizione Perl):

$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print

Si chiama htmlsplit, divide l'HTML in righe, con un tag o un pezzo di testo su ogni riga. Le righe possono quindi essere elaborate ulteriormente con altri strumenti e script di testo, come grep , sed , Perl, ecc. Non sto nemmeno scherzando :) Divertiti.

È abbastanza semplice da rigirare il mio script Perl, tutto sommato, in una bella cosa di streaming, se si desidera elaborare pagine Web enormi. Ma non è davvero necessario.

Scommetto che verrò sottoposto a downgrade per questo.

HTML Split


Contro le mie aspettative questo ha ottenuto alcuni voti, quindi suggerirò alcune espressioni regolari migliori:

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

Sono buoni per XML / XHTML.

Con variazioni minori, può far fronte a HTML disordinato ... o convertire prima l'HTML -> XHTML.


Il modo migliore per scrivere espressioni regolari è nello stile Lex / Yacc , non come una linea opaca o mostruosità multi-linea commentata. Non l'ho ancora fatto qui; questi ne hanno appena bisogno.


35
"Non tento di analizzare HTML idiota che è stato deliberatamente rotto." In che modo il tuo codice conosce la differenza?
Kevin Panko,

Beh, non importa molto se l'HTML è rotto o no. La cosa sarà ancora dividere HTML in tag e testo. L'unica cosa che potrebbe rovinare tutto è se le persone includessero caratteri <o> senza caratteri di escape nel testo o negli attributi. In pratica, il mio piccolo splitter HTML funziona bene. Non ho bisogno di un enorme mostruosità pieno di euristica. Le soluzioni semplici non sono per tutti ...!
Sam Watkins

Ho aggiunto alcune regexps più semplici per l'estrazione di tag, testo e attributi, per XML / XHTML.
Sam Watkins,

(ottieni attributi bug 1) /(\w+)="(.*?)"/assume doppie virgolette. Mancherà i valori tra virgolette singole. Nella versione html 4 è consentito un valore non quotato precedente, se è una parola semplice.
David Andersson,

(ottenere il bug degli attributi 2) /(\w+)="(.*?)"/può corrispondere erroneamente al testo che assomiglia ad un attributo all'interno di un attributo, ad es <img title="Nope down='up' for aussies" src="..." />. Se applicato a livello globale, corrisponderà anche a tali cose nel testo ordinario o nei commenti HTML.
David Andersson,

74

Ecco un parser basato su PHP che analizza HTML usando una regex empia. Come autore di questo progetto, posso dirti che è possibile analizzare HTML con regex, ma non è efficiente. Se hai bisogno di una soluzione lato server (come ho fatto per il mio plugin WordPress wp-Typography ), questo funziona.


1
htmlawed è un altro progetto PHP che analizza HTML per filtrare, convertire, ecc. Ha un bel codice se riesci a capirlo!
user594694,

No, non puoi analizzare HTML con regex. Ma per alcuni sottoinsiemi potrebbe funzionare.
mirabilos,

71

Ci sono alcune belle regex per la sostituzione di HTML con BBCode qui . Per tutti coloro che non conoscono nulla, notate che non sta cercando di analizzare completamente l'HTML, ma solo di disinfettarlo. Probabilmente può permettersi di eliminare i tag che il suo semplice "parser" non può capire.

Per esempio:

$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;

if (!$query->param("ascii")) {
    $html =~ s/\s\s+/\n/gi;
    $html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}

$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;

$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;

$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;

$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;

$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;

15
Non farlo Per favore.
Maletor,

68

Sulla domanda dei metodi RegExp per analizzare (x) HTML, la risposta a tutti coloro che hanno parlato di alcuni limiti è: non sei stato addestrato abbastanza per dominare la forza di questa potente arma, dato che NESSUNO qui ha parlato di ricorsione .

Un collega RegExp-agnostico mi ha informato di questa discussione, che non è certamente la prima sul web su questo argomento vecchio e caldo.

Dopo aver letto alcuni post, la prima cosa che ho fatto è stata cercare la stringa "? R" in questa discussione. Il secondo era cercare "ricorsione".
No, vacca sacra, nessuna corrispondenza trovata.
Dato che nessuno ha menzionato il meccanismo principale su cui è basato un parser, mi sono reso presto conto che nessuno ha capito il punto.

Se un parser (x) HTML necessita di ricorsione, un parser RegExp senza ricorsione non è sufficiente allo scopo. È un costrutto semplice.

L' arte nera di RegExp è difficile da padroneggiare , quindi forse ci sono ulteriori possibilità che abbiamo lasciato fuori mentre provavamo e testavamo la nostra soluzione personale per catturare l'intero web in una mano ... Beh, ne sono sicuro :)

Ecco lo schema magico:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

Provalo e basta.
È scritto come una stringa PHP, quindi il modificatore "s" fa sì che le classi includano newline.
Ecco una nota di esempio sul manuale di PHP che ho scritto a gennaio: Riferimento

(Attenzione, in quella nota ho usato erroneamente il modificatore "m"; dovrebbe essere cancellato, nonostante sia scartato dal motore RegExp, poiché non è stato usato nessun ancoraggio ^ o $).

Ora, potremmo parlare dei limiti di questo metodo da un punto di vista più informato:

  1. in base all'implementazione specifica del motore RegExp, la ricorsione può avere un limite nel numero di schemi nidificati analizzati , ma dipende dalla lingua utilizzata
  2. sebbene HTML (x) corrotto non generi errori gravi, non viene disinfettato .

Comunque è solo un modello RegExp, ma rivela la possibilità di sviluppare molte potenti implementazioni.
Ho scritto questo modello per alimentare il parser di discesa ricorsivo di un motore modello che ho costruito nel mio framework e le prestazioni sono davvero eccezionali, sia nei tempi di esecuzione che nell'uso della memoria (niente a che fare con altri motori modello che usano la stessa sintassi).


35
Lo inserirò nel cestino "Regex che non consente valori maggiori rispetto agli attributi". Controllalo con <input value = "is 5> 3?" />
Gareth,

68
Se inserisci qualcosa del genere nel codice di produzione, verrai probabilmente colpito dal manutentore. Una giuria non lo condannerebbe mai.
aehiilrs,

30
Le espressioni regolari non possono funzionare perché per definizione non sono ricorsive. L'aggiunta di un operatore ricorsivo alle espressioni regolari rende sostanzialmente un CFG solo con sintassi più scadente. Perché non usare qualcosa progettato per essere ricorsivo in primo luogo piuttosto che inserire violentemente la ricorsione in qualcosa che trabocca già di funzionalità estranea?
Welbog,

16
La mia obiezione non è una funzionalità ma è una delle volte investite. Il problema con RegEx è che quando pubblichi il piccolo cutsey sembra che tu abbia fatto qualcosa in modo più efficiente ("Vedi una riga di codice!"). E ovviamente nessuno menziona la mezz'ora (o 3) che hanno trascorso con il loro cheat-sheet e (si spera) testando ogni possibile permutazione di input. E una volta superato tutto ciò quando il manutentore va a capire o convalidare il codice non possono semplicemente guardarlo e vedere che è giusto. Devono dissezionare l'espressione ed essenzialmente riprovare tutto da capo ...
Oorang

15
... sapere che è buono. E ciò accadrà anche con le persone che sono brave con regex. E onestamente sospetto che la stragrande maggioranza delle persone non lo saprà bene. Quindi prendi uno degli incubi di manutenzione più famosi e lo combini con la ricorsione, che è l' altro incubo di manutenzione e penso a me stesso che ciò di cui ho davvero bisogno nel mio progetto sia qualcuno un po 'meno intelligente. L'obiettivo è scrivere codice che i programmatori malintenzionati possono mantenere senza interrompere la base di codice. So che il codice è il minimo comune denominatore. Ma assumere un talento eccellente è difficile e spesso ...
Oorang,

62

Come molte persone hanno già sottolineato, l'HTML non è un linguaggio normale che può rendere molto difficile l'analisi. La mia soluzione a questo è quella di trasformarlo in un linguaggio normale usando un programma ordinato e quindi usare un parser XML per consumare i risultati. Ci sono molte buone opzioni per questo. Il mio programma è scritto usando Java con la libreria jtidy per trasformare l'HTML in XML e quindi Jaxen in xpath nel risultato.


61
<\s*(\w+)[^/>]*>

Le parti spiegate:

<: personaggio iniziale

\s*: potrebbe avere spazi bianchi prima del nome del tag (brutto ma possibile).

(\w+): i tag possono contenere lettere e numeri (h1). Bene, \wcorrisponde anche a "_", ma immagino non faccia male. Se curioso usa invece ([a-zA-Z0-9] +).

[^/>]*: qualsiasi cosa tranne >e /fino alla chiusura>

>: chiusura >

estraneo

E ai compagni che sottovalutano le espressioni regolari dicendo di essere potenti solo come le lingue normali:

a n ba n ba n che non è regolare e nemmeno privo di contesto, può essere abbinato^(a+)b\1b\1$

Indietro riferimento FTW !


@GlitchMr, questo era il suo punto. Le espressioni regolari moderne non sono tecnicamente regolari, né esiste alcun motivo per esserlo.
alanaktion,

3
@alanaktion: Le espressioni regolari "moderne" (leggi: con estensioni Perl) non possono corrispondere all'interno O(MN)(M è la lunghezza dell'espressione regolare, N la lunghezza del testo). Le backreferenze ne sono una delle cause. L'implementazione in awk non ha riferimenti indietro e corrisponde a tutto in O(MN)tempo.
Konrad Borowski,

56

Se stai semplicemente cercando di trovare quei tag (senza ambizioni di analisi) prova questa espressione regolare:

/<[^/]*?>/g

L'ho scritto in 30 secondi e testato qui: http://gskinner.com/RegExr/

Corrisponde ai tipi di tag che hai citato, ignorando i tipi che hai detto di voler ignorare.


2
Penso che intendi \/>invece di \\>.
Justin Morgan,

No, \>è proprio quello che volevo dire; Non ho mai avuto intenzione di modificare l'espressione regolare del mio post originale.
Lonnie Best,

2
Cordiali saluti, non è necessario sfuggire alle parentesi angolari. Certo, non fa male sfuggirli comunque, ma guarda la confusione che avresti potuto evitare. ;)
Alan Moore,

A volte scappo inutilmente quando non sono sicuro che qualcosa sia un personaggio speciale o meno. Ho modificato la risposta; funziona allo stesso modo ma più conciso.
Lonnie Best

Guardando questo ora, non so perché pensassi che intendessi \/, dal momento che farebbe esattamente l'opposto dei requisiti. Forse pensavo che offrissi un modello di filtro negativo.
Justin Morgan,

54

Mi sembra che stai cercando di abbinare i tag senza un "/" alla fine. Prova questo:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

8
Questo non funziona. Per l'ingresso '<xa = "<b>" /> <y>' le corrispondenze sono xey, sebbene x sia terminato.
ceving

51

È vero che durante la programmazione di solito è meglio usare parser e API dedicati anziché espressioni regolari quando si tratta di HTML, soprattutto se l'accuratezza è fondamentale (ad esempio, se l'elaborazione potrebbe avere implicazioni di sicurezza). Tuttavia, non attribuisco a una visione dogmatica che il markup in stile XML non dovrebbe mai essere elaborato con espressioni regolari. Ci sono casi in cui le espressioni regolari sono un ottimo strumento per il lavoro, come quando si effettuano modifiche singole in un editor di testo, si correggono file XML rotti o si gestiscono formati di file che sembrano ma non sono abbastanza XML. Ci sono alcuni problemi da tenere presenti, ma non sono insormontabili o addirittura necessariamente rilevanti.

Un regex semplice come di <([^>"']|"[^"]*"|'[^']*')*>solito è abbastanza buono, in casi come quelli che ho appena menzionato. È una soluzione ingenua, tutto sommato, ma consente correttamente >simboli non codificati nei valori degli attributi. Se stai cercando, ad esempio, un tabletag, puoi adattarlo come </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Solo per dare un'idea di come sarebbe una regex HTML più "avanzata", il seguente fa un lavoro abbastanza rispettabile di emulare il comportamento del browser nel mondo reale e l'algoritmo di analisi HTML5:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

Di seguito corrisponde una definizione abbastanza rigorosa di tag XML (anche se non tiene conto dell'insieme completo di caratteri Unicode consentiti nei nomi XML):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

Certo, questi non tengono conto del contesto circostante e di alcuni casi limite, ma anche queste cose potrebbero essere trattate se lo volessi davvero (ad esempio, cercando tra le partite di un'altra regex).

Alla fine della giornata, utilizzare lo strumento più appropriato per il lavoro, anche nei casi in cui tale strumento risulta essere una regex.


49

Sebbene non sia adatto ed efficace usare espressioni regolari a tale scopo, a volte le espressioni regolari forniscono soluzioni rapide per semplici problemi di corrispondenza e, a mio avviso, non è così orribile usare espressioni regolari per opere banali.

C'è un post sul blog definitivo sulla corrispondenza di elementi HTML più interni scritto da Steven Levithan.

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.