RegEx corrisponde ai tag aperti ad eccezione dei tag XHTML autonomi
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 aver capito bene. Sto solo catturando il file a-z
.
<([a-z]+) *[^/]*?>
Credo che dica:
- Trova un minore di, allora
- Trova (e cattura) az una o più volte, quindi
- Quindi trova zero o più spazi
- Trova un carattere zero o più volte, avido, tranne
/
, allora - Trova un maggiore di
Ho questo diritto? E, cosa più importante, cosa ne pensi?
Risposte
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 l'HTML. Dato che ho risposto a domande su HTML e regex qui tante volte prima, l'uso di regex non ti consentirà di consumare HTML. Le espressioni regolari sono uno strumento che non è sufficientemente sofisticato per comprendere i costrutti impiegati dall'HTML. L'HTML non è un linguaggio normale e quindi non può essere analizzato da espressioni regolari. Le query Regex non sono attrezzate per scomporre l'HTML nelle sue parti significative. tante volte, ma non mi arriva. Anche le espressioni regolari irregolari migliorate usate da Perl non sono all'altezza del compito di analizzare l'HTML. Non mi farai mai crollare. L'HTML è un linguaggio di complessità sufficiente da non poter essere analizzato da espressioni regolari. Anche Jon Skeet non può analizzare l'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 pwnano la tua webapp. L'analisi dell'HTML con l'espressione regolare 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 tanto mastice acquoso. Se analizzi l'HTML con regex ti stai arrendendo a Loro e ai loro modi blasfemi che ci condannano tutti a un lavoro disumano per Colui il cui Nome non può essere espresso nel Piano Multilingue di Base, viene. HTML-plus-regexp fluidificherà i nervi dei senzienti mentre osservi, la tua psiche avvizzirà nell'assalto dell'orrore. I parser HTML basati su Rege̿̔̉x sono il cancro che sta uccidendo StackOverflowè troppo tardiè troppo tardi non possiamo essere salvati la trasgressione di un bambino assicura che la regex consumerà tutto il tessuto vivente (eccetto l'HTML che non può, come precedentemente profetizzato) caro signore aiutaci come può qualcuno 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 rottura tra questo mondo e il terribile regno delle entità distrutte (come le entità SGML, ma più corrotte) un semplice barlume 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, arriva
, 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 tutta l'illuminazione, i tag HTML che fuoriesce dalla yo ur occhi come liq uid p ain, il canto di regolare exp re
ssione analisi
saranno EXTI nguish le voci di Mor uomo Tal, dalla sp qui posso vederlo si può vedere che è bello t ha f inal snuf
fing o f la menzogna s dell'uomo tutto è perduto A LL ho SL OST esimo e pony che vengo s ha com
es ha co
me
st lui 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 n ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈reviews ̀C̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ
͎a̧͈͖r̽̾̈́͒͑e
Hai provato a utilizzare un parser XML invece?
Nota del moderatore
Questo post è bloccato per impedire modifiche inappropriate al suo contenuto. Il post sembra esattamente come dovrebbe apparire: non ci sono problemi con il suo contenuto. Si prega di non segnalarlo alla nostra attenzione.
Sebbene HTML arbitrario con solo un'espressione regolare sia impossibile, a volte è appropriato utilizzarli per analizzare un insieme limitato e noto di HTML.
Se si dispone di un piccolo insieme di pagine HTML da cui si desidera estrarre i dati e quindi inserirli in un database, le espressioni regolari potrebbero funzionare bene. Ad esempio, di recente 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 e una tantum.
Le espressioni regolari funzionavano bene per me ed erano molto veloci da configurare.
Penso che il difetto qui sia che l'HTML è una grammatica Chomsky di tipo 2 (grammatica libera dal contesto) e un'espressione regolare è una grammatica di Chomsky di tipo 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 un'espressione regolare.
Ma molti ci proveranno, e alcuni addirittura rivendicheranno il successo, ma fino a quando altri non troveranno la colpa e ti incasineranno completamente.
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 esegue ciascuna di queste operazioni in ordine:
- Risolvi il problema dell'arresto.
- Piazza un cerchio.
- Risolvi il problema del commesso viaggiatore in O (log n) o meno. Se è più di questo, finirai la RAM e il motore si bloccherà.
- Il modello sarà piuttosto grande, quindi assicurati di avere un algoritmo che comprima i dati casuali senza perdite.
- Ci siamo quasi - dividi tutto per zero. Vai tranquillo.
Non ho ancora finito l'ultima parte da solo, ma so che mi sto avvicinando. Continua a lanciare CthulhuRlyehWgahnaglFhtagnException
s per qualche motivo, quindi lo trasferirò su VB 6 e lo userò On Error Resume Next
. Aggiornerò con il codice una volta che indagherò su questa strana porta che si è appena aperta nel muro. Hmm.
PS Anche Pierre de Fermat ha capito come farlo, ma il margine in cui scriveva non era abbastanza grande per il codice.
Disclaimer : usa un parser se ne hai la possibilità. Detto ciò...
Questa è la regex che uso (!) Per abbinare i tag HTML:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
Potrebbe non essere perfetto, ma ho eseguito questo codice attraverso molto HTML. Nota che cattura anche cose strane come <a name="badgenerator"">
, che compaiono sul web.
Immagino che per fare in modo che non corrisponda ai tag autonomi, dovresti utilizzare il look-behind negativo di Kobi :
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
o semplicemente combina se e se no.
Per i downvoters: questo codice funziona da un prodotto reale. Dubito che chiunque legga questa pagina avrà l'impressione che sia socialmente accettabile utilizzare le espressioni regolari su HTML.
Avvertenza : dovrei notare che questa regex si interrompe ancora in presenza di blocchi CDATA, commenti ed elementi di script e stile. La buona notizia è che puoi sbarazzarti di quelli che usano un'espressione regolare ...
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 sottometterti e lo fanno tenendoti nell'ignoranza.
Puoi vivere nella loro realtà o prendere la pillola rossa.
Come Lord Marshal (è un parente della classe Marshal .NET?), Ho visto il
Regex-Verse basato
sullo stack underverso
e sono tornato con una
conoscenza di
poteri
che non puoi 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. La RegEx (nella sintassi .NET), deflazionata e codificata in base64 per renderla più facile da comprendere alla tua mente debole, dovrebbe essere qualcosa del genere:
7L0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28
995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8itn6Po9/3eIue3+Px7/3F
86enJ8+/fHn64ujx7/t7vFuUd/Dx65fHJ6dHW9/7fd/t7fy+73Ye0v+f0v+Pv//JnTvureM3b169
OP7i9Ogyr5uiWt746u+BBqc/8dXx86PP7tzU9mfQ9tWrL18d3UGnW/z7nZ9htH/y9NXrsy9fvPjq
i5/46ss3p4z+x3e8b452f9/x93a2HxIkH44PpgeFyPD6lMAEHUdbcn8ffTP9fdTrz/8rBPCe05Iv
p9WsWF788Obl9MXJl0/PXnwONLozY747+t7x9k9l2z/4vv4kqo1//993+/vf2kC5HtwNcxXH4aOf
LRw2z9/v8WEz2LTZcpaV1TL/4c3h66ex2Xv95vjF0+PnX744PbrOm59ZVhso5UHYME/dfj768H7e
Yy5uQUydDAH9+/4eR11wHbqdfPnFF6cv3ogq/V23t++4z4620A13cSzd7O1s/77rpw+ePft916c7
O/jj2bNnT7e/t/397//M9+ibA/7s6ZNnz76PP0/kT2rz/Ts/s/0NArvziYxVEZWxbm93xsrUfnlm
rASN7Hf93u/97vvf+2Lx/e89L7+/FSXiz4Bkd/hF5mVq9Yik7fcncft9350QCu+efkr/P6BfntEv
z+iX9c4eBrFz7wEwpB9P+d9n9MfuM3yzt7Nzss0/nuJfbra3e4BvZFR7z07pj3s7O7uWJM8eCkme
nuCPp88MfW6kDeH7+26PSTX8vu+ePAAiO4LVp4zIPWC1t7O/8/+pMX3rzo2KhL7+8s23T1/RhP0e
vyvm8HbsdmPXYDVhtpdnAzJ1k1jeufOtUAM8ffP06Zcnb36fl6dPXh2f/F6nRvruyHfMd9rgJp0Y
gvsRx/6/ZUzfCtX4e5hTndGzp5jQo9e/z+s3p1/czAUMlts+P3tz+uo4tISd745uJxvb3/v4ZlWs
mrjfd9SG/swGPD/6+nh+9MF4brTBRmh1Tl5+9eT52ckt5oR0xldPzp7GR8pfuXf5PWJv4nJIwvbH
W3c+GY3vPvrs9zj8Xb/147/n7/b7/+52DD2gsSH8zGDvH9+i9/fu/PftTfTXYf5hB+9H7P1BeG52
MTtu4S2cTAjDizevv3ry+vSNb8N+3+/1po2anj4/hZsGt3TY4GmjYbEKDJ62/pHB+3/LmL62wdsU
1J18+eINzTJr3dMvXr75fX7m+MXvY9XxF2e/9+nTgPu2bgwh5U0f7u/74y9Pnh6/OX4PlA2UlwTn
xenJG8L996VhbP3++PCrV68QkrjveITxr2TIt+lL+f3k22fPn/6I6f/fMqZvqXN/K4Xps6sazUGZ
GeQlar49xEvajzI35VRevDl78/sc/b7f6jkG8Va/x52N4L9lBe/kZSh1hr9fPj19+ebbR4AifyuY
12efv5CgGh9TroR6Pj2l748iYxYgN8Z7pr0HzRLg66FnRvcjUft/45i+pRP08vTV6TOe2N/9jv37
R9P0/5YxbXQDeK5E9R12XdDA/4zop+/9Ht/65PtsDVlBBUqko986WsDoWqvbPD2gH/T01DAC1NVn
3/uZ0feZ+T77fd/GVMkA4KjeMcg6RcvQLRl8HyPaWVStdv17PwHV0bOB9xUh7rfMp5Zu3icBJp25
D6f0NhayHyfI3HXHY6YYCw7Pz17fEFhQKzS6ZWChrX+kUf7fMqavHViEPPKjCf1/y5hukcyPTvjP
mHQCppRDN4nbVFPaT8+ekpV5/TP8g/79mVPo77PT1/LL7/MzL7548+XvdfritflFY00fxIsvSQPS
mvctdYZpbt7vxKRfj3018OvC/hEf/79lTBvM3debWj+b8KO0wP+3OeM2aYHumuCAGonmCrxw9cVX
X1C2d4P+uSU7eoBUMzI3/f9udjbYl/el04dI7s8fan8dWRjm6gFx+NrKeFP+WX0CxBdPT58df/X8
DaWLX53+xFdnr06f/szv++NnX7x8fnb6NAhIwsbPkPS7iSUQAFETvP2Tx8+/Og0Xt/yBvDn9vd/c
etno8S+81QKXptq/ffzKZFZ+4e/743e8zxino+8RX37/k595h5/H28+y7fPv490hQdJ349E+txB3
zPZ5J/jsR8bs/y1j2hh/2fkayOqEmYcej0cXUWMN7QrqBwjDrVZRfyQM3xjj/EgYvo4wfLTZrnVS
ebdKq0XSZJvzajKQDUv1/P3NwbEP7cN5+Odivv9/ysPfhHfkOP6b9Fl+91v7LD9aCvp/+Zi+7lLQ
j0zwNzYFP+/Y6r1NcFeDbfBIo8rug3zS3/3WPumPlN3/y8f0I2X3cz4FP+/Y6htSdr2I42fEuSPX
/ewpL4e9/n1evzn94hb+Plpw2+dnbyh79zx0CsPvbq0lb+UQ/h7xvqPq/Gc24PnR18fzVrp8I57d
mehj7ebk5VdPnp+d3GJOSP189eTsaXyk/JV7l98j4SAZgRxtf7x155PR+O6jz36Pw9/1Wz/+e/5u
v//vbsfQAxobws8M9v7xLXp/785/395ED4nO1wx5fsTeH4LnRva+eYY8rpZUBFb/j/jfm8XAvfEj
4/b/ljF1F9B/jx5PhAkp1nu/+y3n+kdZp/93jWmjJ/M11TG++VEG6puZn593PPejoOyHMQU/79jq
GwrKfpSB+tmcwZ93XPkjZffDmIKfd2z1DSm7bmCoPPmjBNT74XkrVf71I/Sf6wTU7XJA4RB+lIC6
mW1+xN5GWw1/683C5rnj/m364cmr45Pf6/SN9H4Us4LISn355vjN2ZcvtDGT6fHvapJcMISmxc0K
MAD4IyP6/5Yx/SwkP360FvD1VTH191mURr/HUY+2P3I9boPnz7Ju/pHrcWPnP3I9/r/L3sN0v52z
0fEgNrgbL8/Evfh9fw/q5Xf93u/97vvf+2Lx/e89L7+/Fe3iZ37f34P5h178kTfx/5YxfUs8vY26
7/d4/OWbb5++ogn7PX5XzOHtOP3GrsHmqobOVO/8Hh1Gk/TPl198QS6w+rLb23fcZ0fMaTfjsv29
7Zul7me2v0FgRoYVURnf9nZEkDD+H2VDf8hjeq8xff1s6GbButNLacEtefHm9VdPXp++CRTw7/v9
r6vW8b9eJ0+/PIHzs1HHdyKE/x9L4Y+s2f+PJPX/1dbsJn3wrY6wiqv85vjVm9Pnp+DgN8efM5va
j794+eb36Xz3mAf5+58+f3r68s230dRvJcxKn/l//oh3f+7H9K2O0r05PXf85s2rH83f/1vGdAvd
w+qBFqsoWvzspozD77EpXYeZ7yzdfxy0ec+l+8e/8FbR84+Wd78xbvn/qQQMz/J7L++GPB7N0MQa
2vTMBwjDrVI0PxKGb4xxfiQMX0cYPuq/Fbx2C1sU8yEF+F34iNsx1xOGa9t6l/yX70uqmxu+qBGm
AxlxWwVS11O97ULqlsFIUvUnT4/fHIuL//3f9/t9J39Y9m8W/Tuc296yUeX/b0PiHwUeP1801Y8C
j/9vz9+PAo8f+Vq35Jb/n0rAz7Kv9aPA40fC8P+RMf3sC8PP08DjR1L3DXHoj6SuIz/CCghZNZb8
fb/Hf/2+37tjvuBY9vu3jmRvxNeGgQAuaAF6Pwj8/+e66M8/7rwpRNj6uVwXZRl52k0n3FVl95Q+
+fz0KSu73/dtkGDYdvZgSP5uskadrtViRKyal2IKAiQfiW+FI+tET/9/Txj9SFf8SFf8rOuKzagx
+r/vD34mUADO1P4/AQAA//8=
Le opzioni da impostare sono RegexOptions.ExplicitCapture
. Il gruppo di cattura che stai cercando è ELEMENTNAME
. Se il gruppo di acquisizione ERROR
non è vuoto, si è verificato un errore di analisi e il 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 unit test per testarlo e ho persino usato (parte di) i test di conformità . È un tokenizer, non un parser completo, quindi dividerà solo l'XML nei suoi token componenti. Non analizzerà / integrerà DTD.
Oh ... se vuoi il codice sorgente della regex, con alcuni metodi ausiliari:
regex per tokenizzare un xml o l'intera semplice regex
Nella shell, puoi analizzare l' HTML usando sed :
- Turing.sed
- Scrivi un parser HTML (compiti a casa)
- ???
- Profitto!
Correlati (perché non dovresti usare la corrispondenza regex):
- Se ti piacciono così tanto le espressioni regolari, perché non le sposi?
- Espressioni regolari: ora hai due problemi
- Hacking del disinfettante HTML di stackoverflow.com
Sono d'accordo che lo strumento giusto per analizzare XML e soprattutto HTML sia un parser e non un motore di espressioni regolari. Tuttavia, come altri hanno sottolineato, a volte l'uso di una regex è più veloce, più facile e porta a termine il lavoro se conosci il formato dei dati.
Microsoft ha in realtà una sezione delle migliori pratiche per le espressioni regolari in .NET Framework e parla specificamente di Consider [ing] the Input Source .
Le espressioni regolari hanno dei limiti, ma hai considerato quanto segue?
Il framework .NET è unico quando si tratta di espressioni regolari in quanto supporta il bilanciamento delle definizioni dei gruppi .
- Vedere Corrispondenza di costrutti bilanciati con espressioni regolari .NET
- Vedere .NET Regular Expressions: Regex and Balanced Matching
- Vedere la documentazione di Microsoft sulle definizioni dei gruppi di bilanciamento
Per questo motivo, credo che tu possa analizzare XML utilizzando espressioni regolari. Si noti tuttavia che deve essere 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 delle espressioni regolari di agire come un PDA.
Citazione dall'articolo 1 sopra citato:
.NET Regular Expression Engine
Come descritto sopra, i costrutti adeguatamente bilanciati non possono essere descritti da un'espressione regolare. Tuttavia, il motore delle espressioni regolari .NET fornisce alcuni costrutti che consentono il riconoscimento dei costrutti bilanciati.
(?<group>)
- inserisce il risultato acquisito nello stack di acquisizione con il gruppo di nomi.(?<-group>)
- fa apparire la prima cattura con il nome del gruppo fuori dallo stack di cattura.(?(group)yes|no)
- corrisponde alla parte sì se esiste un gruppo con il nome gruppo altrimenti non corrisponde a nessuna parte.Questi costrutti consentono a un'espressione regolare .NET di emulare un PDA limitato consentendo essenzialmente versioni semplici delle operazioni sullo stack: push, pop e svuota. Le operazioni semplici sono praticamente equivalenti rispettivamente a incrementare, decrementare e confrontare a zero. Ciò consente al motore delle espressioni regolari .NET di riconoscere un sottoinsieme dei linguaggi privi di contesto, in particolare quelli che richiedono solo un contatore semplice. Ciò a sua volta consente alle espressioni regolari .NET non tradizionali di riconoscere i singoli costrutti adeguatamente 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 comprimi regex e rimuovi tutti gli spazi bianchi)
- IgnoreCase (non necessario)
Spiegazione dell'espressione regolare (inline)
(?=<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 >ul<, 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 >ul<, 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 >ul<, 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 più di 4k voti.
Suggerisco di utilizzare QueryPath per analizzare XML e HTML in PHP. È fondamentalmente più o meno la stessa sintassi di jQuery, solo che è sul lato server.
Sebbene le risposte che non puoi analizzare HTML con le espressioni regolari siano corrette, non si applicano qui. L'OP vuole solo analizzare un tag HTML con espressioni regolari, e questo è qualcosa che può essere fatto con un'espressione regolare.
La regex suggerita è sbagliata, però:
<([a-z]+) *[^/]*?>
Se aggiungi qualcosa alla regex, tornando indietro può essere forzato ad abbinare 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 espressioni regolari Perl) il look-behind negativo. Si legge "a <, poi una parola, quindi tutto ciò che non è a>, l'ultima delle quali potrebbe non essere una /, seguita da>".
Nota che questo consente cose come <a/ >
(proprio come la regex originale), quindi se vuoi qualcosa di più restrittivo, devi creare una regex per abbinare le coppie di attributi separate da spazi.
Provare:
<([^\s]+)(\s[^>]*?)?(?<!/)>
È simile al tuo, ma l'ultimo >
non deve essere dopo una barra e accetta anche h1
.
Sun Tzu, un antico stratega cinese, generale e filosofo, ha detto:
Si dice che se conosci i tuoi nemici e conosci te stesso, puoi vincere cento 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 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 delle espressioni regolari in Perl.
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
<?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 automaticamente, caricare l'intera stringa html in una libreria DOM, afferrare tutti gli elementi, scorrere e filtrare quelli che non si chiudono automaticamente e operare su di essi.
Sono sicuro che ormai sai già che non dovresti usare regex per questo scopo.
Non conosco esattamente la tua esigenza, ma se stai usando anche .NET, non potresti usare Html Agility Pack ?
Estratto:
È una libreria di codice .NET che consente di analizzare file HTML "fuori dal Web". Il parser è molto tollerante con HTML malformato "nel mondo reale".
Vuoi che il primo >
non sia preceduto da a /
. Guarda qui per i dettagli su come farlo. È indicato come 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 iterando i tag in modo programmatico?
Il W3C spiega l'analisi in una forma pseudo regexp:
W3C Link
Segui i link per var QName
, S
e Attribute
per avere un quadro più chiaro.
Sulla base di ciò puoi creare un'espressione regolare abbastanza buona per gestire cose come lo stripping dei tag.
Se ti serve per PHP:
Le funzioni PHP DOM non funzioneranno correttamente a meno che non sia XML formattato correttamente. Non importa quanto sia migliore il loro uso per il resto dell'umanità.
simplehtmldom è buono, ma l'ho trovato un po ' pieno di bug, ed è piuttosto pesante per la memoria [si bloccherà su pagine grandi.]
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 lo uso felicemente da un po '. Semplice da imparare e potente.
Per Python e Java, sono stati pubblicati collegamenti simili.
Per i downvoters: ho scritto la mia classe solo quando i parser XML si sono dimostrati incapaci di resistere all'uso reale. Il downvoting religioso impedisce semplicemente che vengano pubblicate risposte utili: mantieni le cose nella prospettiva della domanda, per favore.
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 nella stringa tag di chiusura automatica come:
- <hr />
- <br/>
- <br>
Ho anche inserito tag con:
- un attributo
- più di un attributo
- attributi il cui valore è racchiuso tra virgolette singole o doppie
- attributi contenenti virgolette singole quando il delimitatore è virgolette doppie e viceversa
- "unpretty" si attribuisce con uno spazio prima del simbolo "=", dopo di esso e sia prima che dopo di esso.
Se dovessi trovare qualcosa che non funziona nella prova di 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 lo schema è più semplice, trasformandosi in questo:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
L'utente @ridgerunner ha notato che il pattern non consente attributi non quotati o attributi senza valore . In questo caso una messa a punto fine ci porta il seguente schema:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
</EDIT>
Capire lo schema
Se qualcuno è interessato a saperne di più sul pattern, fornisco alcune righe:
- la prima sottoespressione (\ w +) corrisponde al nome del tag
- la seconda sottoespressione contiene il modello di un attributo. È composto da:
- uno o più spazi bianchi \ s +
- il nome dell'attributo (\ w +)
- zero o più spazi bianchi \ s * (è possibile o no, lasciare spazi vuoti qui)
- il simbolo "="
- di nuovo, zero o più spazi bianchi
- il delimitatore del valore dell'attributo, virgolette singole o doppie ('| "). Nel pattern, le virgolette singole sono scappate perché coincidono 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.
- il valore dell'attributo, corrispondente a quasi tutto: (. *?); in questa specifica sintassi, usando la corrispondenza greedy (il punto interrogativo dopo l'asterisco) il motore RegExp abilita un operatore "look-ahead", che corrisponde a qualsiasi cosa tranne ciò che segue questa sottoespressione
- ecco che arriva il bello: la parte \ 4 è un operatore di backreference , che si riferisce a una sottoespressione definita prima nel pattern, in questo caso, mi riferisco alla quarta sottoespressione, che è il primo delimitatore di attributo trovato
- zero o più spazi bianchi \ s *
- la sottoespressione dell'attributo termina qui, con la specificazione di zero o più occorrenze possibili, data dall'asterisco.
- Quindi, poiché un tag può terminare con uno spazio bianco prima del simbolo ">", zero o più spazi bianchi vengono abbinati al sottopattern \ s *.
- Il tag da abbinare può terminare con un semplice simbolo ">", o una possibile chiusura XHTML, che fa uso della barra che lo precede: (/> |>). 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.
Ogni volta che ho bisogno di 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']
Prima ho usato uno strumento open source chiamato HTMLParser . È progettato per analizzare l'HTML in vari modi e serve allo scopo abbastanza bene. Può analizzare l'HTML come diversi treenode e puoi facilmente usare la sua API per ottenere attributi dal nodo. Dai un'occhiata e vedi se questo può aiutarti.
Mi piace analizzare l'HTML con espressioni regolari. Non cerco di analizzare HTML idiota che è 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 una porzione di testo su ogni riga. Le righe possono quindi essere elaborate ulteriormente con altri strumenti di testo e script, come grep , sed , Perl, ecc. Non sto nemmeno scherzando :) Buon divertimento.
È abbastanza semplice reimpostare il mio script Perl slurp-everything-first in una bella cosa di streaming, se desideri elaborare enormi pagine web. Ma non è davvero necessario.
HTML Split
Alcune espressioni regolari migliori:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
Sono buoni per XML / XHTML.
Con piccole variazioni, 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 one-line opache o mostruosità su più righe commentate. Non l'ho ancora fatto qui; questi ne hanno appena bisogno.
Ecco un parser basato su PHP che analizza l'HTML usando alcune regex empie. 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 ), funziona.
Ci sono alcune belle espressioni regolari per sostituire HTML con BBCode qui . Per tutti voi contrari, nota che non sta cercando di analizzare completamente l'HTML, solo per disinfettarlo. Probabilmente può permettersi di eliminare tag che il suo semplice "parser" non riesce a 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;
Riguardo alla questione dei metodi di espressione regolare per analizzare (x) HTML, la risposta a tutti coloro che hanno parlato di alcuni limiti è: non sei stato addestrato abbastanza per governare la forza di questa potente arma, poiché nessuno qui ha parlato di ricorsione .
Un collega indipendente dalle espressioni regolari mi ha notificato questa discussione, che non è certamente la prima sul web su questo vecchio e caldo argomento.
Dopo aver letto alcuni post, la prima cosa che ho fatto è stata cercare la stringa "? R" in questo thread. Il secondo era cercare di "ricorsione".
No, vacca sacra, nessuna corrispondenza trovata. Dal momento che nessuno ha menzionato il meccanismo principale su cui è costruito un parser, mi sono subito reso conto che nessuno aveva capito il punto.
Se un parser HTML (x) necessita di ricorsione, un parser di espressioni regolari senza ricorsione non è sufficiente allo scopo. È un semplice costrutto.
L' arte nera delle espressioni regolari è difficile da padroneggiare , quindi forse ci sono ulteriori possibilità che abbiamo tralasciato mentre provavamo e testavamo la nostra soluzione personale per catturare l'intero web in una mano ... beh, ne sono sicuro :)
Ecco il modello magico:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
Provalo e basta. È scritto come una stringa PHP, quindi il modificatore "s" fa in modo che le classi includano le nuove righe.
Ecco una nota di esempio sul manuale 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 delle espressioni regolari, dato che non è stato usato nessun ^
o $
ancoraggio).
Ora, potremmo parlare dei limiti di questo metodo da un punto di vista più informato:
- secondo l'implementazione specifica del motore delle espressioni regolari, la ricorsione può avere un limite nel numero di modelli annidati analizzati , ma dipende dal linguaggio utilizzato
- sebbene danneggiato, (x) HTML non causa errori gravi. Non è igienizzato .
In ogni caso, è solo un pattern di espressioni regolari, ma rivela la possibilità di sviluppare molte potenti implementazioni.
Ho scritto questo modello per potenziare il parser di discesa ricorsivo di un motore di modelli che ho costruito nel mio framework e le prestazioni sono davvero eccezionali, sia nei tempi di esecuzione che nell'utilizzo della memoria (niente a che fare con altri motori di modelli che usano la stessa sintassi).
Come molte persone hanno già sottolineato, l'HTML non è un linguaggio normale che può renderlo molto difficile da analizzare. La mia soluzione è trasformarlo in un linguaggio normale utilizzando un programma ordinato e quindi utilizzare 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 poi Jaxen in xpath nel risultato.
<\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). Beh, \w
corrisponde anche a "_", ma immagino non faccia male. Se sei curioso, usa ([a-zA-Z0-9] +) invece.
[^/>]*
: Tutto tranne >
e /
fino alla chiusura>
>
: Chiusura >
NON CORRELATO
E ai ragazzi, che sottovalutano le espressioni regolari, dicendo che sono potenti quanto le lingue normali:
un n ba n ba n che non è regolare e nemmeno libero dal contesto, può essere abbinato^(a+)b\1b\1$
Backreferencing FTW !
Se stai semplicemente cercando di trovare quei tag (senza ambizioni di analisi) prova questa espressione regolare:
/<[^/]*?>/g
L'ho scritto in 30 secondi e l'ho provato qui: http://gskinner.com/RegExr/
Corrisponde ai tipi di tag che hai menzionato, ignorando i tipi che hai detto di voler ignorare.
Mi sembra che tu stia cercando di abbinare i tag senza "/" alla fine. Prova questo:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
È vero che durante la programmazione di solito è meglio usare parser e API dedicati invece di espressioni regolari quando si ha a che fare con HTML, specialmente se l'accuratezza è fondamentale (ad esempio, se l'elaborazione potrebbe avere implicazioni sulla 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 apportano modifiche una tantum in un editor di testo, si riparano file XML danneggiati o si ha a che fare con formati di file che sembrano ma non sono proprio XML. Ci sono alcuni problemi di cui essere consapevoli, ma non sono insormontabili né necessariamente rilevanti.
Una semplice regex come di <([^>"']|"[^"]*"|'[^']*')*>
solito è abbastanza buona, 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 table
tag, puoi adattarlo come </?table\b([^>"']|"[^"]*"|'[^']*')*>
.
Solo per dare un'idea di come sarebbe una regex HTML più "avanzata", quanto segue fa un lavoro abbastanza rispettabile di emulazione del comportamento del browser del mondo reale e l'algoritmo di analisi HTML5:
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
Quanto segue corrisponde a una definizione abbastanza rigorosa di tag XML (sebbene non tenga conto del set 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 risolte se lo si desidera davvero (ad esempio, cercando tra le corrispondenze di un'altra regex).
Alla fine della giornata, usa lo strumento più appropriato per il lavoro, anche nei casi in cui quello strumento è una regex.
Sebbene non sia adatto ed efficace utilizzare espressioni regolari a tale scopo, a volte le espressioni regolari forniscono soluzioni rapide per semplici problemi di corrispondenza e, a mio avviso, non è poi così orribile utilizzare espressioni regolari per lavori banali.
C'è un post sul blog definitivo sulla corrispondenza degli elementi HTML più interni scritti da Steven Levithan.