Domanda
Sto cercando di recuperare i tag specifici con il loro contenuto di un documento xhtml, ma è un abbinamento sbagliato di fine tag.
Il seguente contenuto:
<cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
Il content_block fine tag per id="benvenuto" in realtà è abbinato come la fine tag della prima apertura content_block tag.
La regex che sto usando è:
/<content_block id="(.*)">([\w\W]*?)<\/content_block>/i
Puntatori come dove sto fallendo?
Soluzione
... e la risposta è sempre la stessa: HTML + espressione regolare non può essere fatto . Scusate. Utilizzare una libreria di parsing del codice HTML per il vostro quadro particolare. Oppure, se il documento è garantito per contenere solo XHTML valido, prendere l'approccio XPath come proposto da jitter in un commento.
Altri suggerimenti
questo potrebbe aiutare ho trovato tutorial su http://www.regular-expressions.info/examples.html che menziona la cattura paio di stringa ricorrenti nel testo dato. suggerimento è quello di usare? dopo. * farlo smettere dopo la prima occorrenza di finire stringa della coppia nel testo
Questo è un problema noto con regex - Non è possibile abbinare le coppie. Matching è sia avido, in cui si abbina l'ultima che trova, o non avido, in cui corrisponde il primo. Non si può convincere un regex per contare parentesi di apertura e chiusura.
mi sento di raccomandare di caricarle in un DOM e utilizzando tale. Se si sta cercando di implementare un parser HTML, mi consiglia di utilizzare espressioni regolari per lex, poi un parser sinistra-destra per analizzare l'output del lexer.
grazie a @Jan Żankowski e @ikegami, la loro risposta mi ha dato inpiration
Mi permetta di usare PHP per dimostrare il codice
<?php
$xml = <<<EOT
<cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
EOT;
preg_match('/<cache_namespace[^>]+>((?:(?!(<\/?div>)).)*)<\/cache_namespace>/s', $xml, $matches);
print_r($matches);
regex nota
s
opzione:un.
nel modello corrisponde a tutti i caratteri, compresi i ritorni a capo- La chiave qui è che
(?:(?!STRING).)*
è per stringhe[^CHAR]*
è caratteri
risultato
Array
(
[0] => <cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
[1] =>
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
)
Analisi XHTML o XML non è difficile. Ho pensato che si dispone di codice valido o ben formato.
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;
my $xml = <<"EOF";
<cache_namespace name="content">
<content_block id="15">
some content here
<cache_namespace name="user">
<content_block id="welcome">
Welcome Apikot!
</content_block>
</cache_namespace>
</content_block>
</cache_namespace>
EOF
while ($xml =~ m!
<(content_block)\sid="welcome"> # Start tag definition.
(\s* # It may consists of
(?: <\!--.*?--> # - comment
| [^<]* # - text
| <[^>]+/> # - another closed tag
| <\s*(\w+)[^>]*> # - another tag with some content
(?2)+ # (recursive definition of possible tag content)
</\3>
)
)*
</\1>
!sxgc) {
print "==> $&\n\n";
}
Si prega di modificare la definizione di tag di inizio per un altro contenuto (come <\s*(\w+)[^>]*+>
). Comunque è un buon punto di partenza.
Se non si utilizza la ricorsione (linea con (?2)+
) si bloccato su esempi . Questo codice tutti in grado di gestire (si prega di guardare qui prima) o può adattare facilmente per nuove situazioni.