2009-02-22 13 views
8

data una stringa come questa:Come faccio a far corrispondere il testo in HTML che non è all'interno dei tag?

<a href="http://blah.com/foo/blah">This is the foo link</a> 

... e una stringa di ricerca come "pippo", vorrei evidenziare tutte le occorrenze di "pippo" nel testo del HTML - ma non all'interno di una etichetta. In altre parole, voglio ottenere questo:

<a href="http://blah.com/foo/blah">This is the <b>foo</b> link</a> 

Tuttavia, una semplice ricerca e sostituzione non funzionerà, perché corrisponderà parte dell'URL nel < a> href del tag.

Quindi, per esprimere quanto sopra nella forma di una domanda: Come limitare una regex in modo che corrisponda solo al testo al di fuori dei tag HTML?

Nota: Vi prometto che il codice HTML in questione non sarà mai nulla di patologico simile:

<img title="Haha! Here are some angle brackets to screw you up: ><" /> 

Edit: Sì, certo mi rendo conto che ci sono le librerie complesse in CPAN in grado di analizzare anche l'HTML più atroce, e quindi alleviare la necessità di una regex di questo tipo. In molte occasioni, è quello che userei. Tuttavia, questa non è una di quelle occasioni, dal momento che mantenere questo script breve e semplice, senza dipendenze esterne, è importante. Voglio solo una regex di una sola riga.

Modifica 2: Ancora una volta, so che Template :: Refine :: Fragment può analizzare tutto il mio codice HTML per me. Se scrivessi un'applicazione , utilizzerei sicuramente una soluzione del genere. Ma questa non è un'applicazione. È a malapena più di uno script di shell. È un pezzo di codice usa e getta. Essendo un singolo file autonomo che può essere passato in giro è di grande valore in questo caso. "Ehi, esegui questo programma" è un'istruzione molto più semplice di "Ehi, installa un modulo Perl e poi esegui questo - aspetta, cosa, non hai mai usato CPAN prima?" Ok, esegui perl -MCPAN -e shell (preferibilmente come root) e poi ti farà un sacco di domande, ma non hai davvero bisogno di rispondere loro No, non aver paura, questo non spezzerà nulla. Guarda, non hai bisogno per rispondere con attenzione a ogni domanda, basta premere Invio più volte No, lo prometto, non spezzerà nulla ".

Ora moltiplica quanto sopra per una grande quantità di utenti che si stanno chiedendo perché il semplice script che hanno utilizzato non sia più così semplice, quando tutto ciò che è cambiato è rendere il termine di ricerca in grassetto.

Così mentre Template :: Refine :: Fragment può essere la risposta alla domanda di analisi HTML di qualcun altro, non è la risposta a questa domanda. Voglio solo un'espressione regolare che lavori sul sottoinsieme molto limitato dell'HTML che verrà effettivamente richiesto allo script di analizzare.

risposta

10

Se si può assolutamente garantire che non ci sono parentesi angolari nel codice HTML diversi da quelli utilizzati per aprire e chiudere i tag, questo dovrebbe funzionare:

s%(>|\G)([^<]*?)($key)%$1$2<b>$3</b>%g 
+1

Vero ... questo è parte del motivo per cui gli altri stanno dicendo che dovresti davvero usare un parser HTML piuttosto che una semplice regex. E in realtà sono d'accordo con loro, ma se vuoi davvero usare s /// allora buttati fuori ;-) –

+0

Questi sono tutti rotti.Prova a evidenziare "foo" in "foo foo blabla foo \ n fooo" – vladr

+0

Reinventare la ruota è così divertente! – jrockway

7

In generale, si desidera analizzare l'HTML in un DOM e quindi attraversare i nodi di testo. Vorrei usare Template :: Affina per questo:

#!/usr/bin/env perl 

use strict; 
use warnings; 
use feature ':5.10'; 

use Template::Refine::Fragment; 

my $frag = Template::Refine::Fragment->new_from_string('<p>Hello, world. <a href="http://foo.com/">This is a test of foo finding.</a> Here is another foo.'); 

say $frag->process(
    simple_replace { 
     my $n = shift; 
     my $text = $n->textContent; 
     $text =~ s/foo/<foo>/g; 
     return XML::LibXML::Text->new($text); 
    } '//text()', 
)->render; 

Questo uscite:

<p>Hello, world. <a href="http://foo.com/">This is a test of &lt;foo&gt; finding.</a> Here is another &lt;foo&gt;.</p> 

Comunque, non analizzare i dati strutturati con le espressioni regolari. L'HTML non è "regolare", è "privo di contesto".

Modifica: infine, se si sta generando l'HTML all'interno del programma e si devono eseguire trasformazioni come questa sulle stringhe, "UR DOIN IT WRONG". Dovresti creare un DOM e serializzarlo solo quando tutto è stato trasformato.(. È comunque possibile utilizzare TR, però, tramite il costruttore new_from_dom)

+0

Va bene, ma sto autogenerating tutto il codice HTML (1?). È estremamente semplice HTML. Non posso in buona fede giustificare l'introduzione di un'intera libreria dei pesi massimi solo per dare uno schiaffo ai tag in grassetto attorno ad alcune stringhe. – raldi

+0

Fai quello che vuoi. Il mio tempo non è sprecato quando reinventate una ruota quadrata. (L'analisi dell'HTML con espressioni regolari è molto difficile. Come mostrano i tuoi esempi, è difficile ottenere il risultato corretto.) – jrockway

+0

I regessi falliscono quando si considerano i commenti e le sezioni CDATA. (I parser basati su Regex vanno bene, ma è necessario archiviare più stati che le espressioni regex possono essere archiviati da soli.È per questo che si ha un parser invece di un'espressione regolare casuale . – jrockway

2

La seguente espressione regolare corrisponderà tutto il testo tra i tag o al di fuori dei tag:

<.*?>(.*?)<.*?>|>(.*?)< 

Quindi è possibile operare su quella, se lo desideri.

0

Provate questo

(?=>)?(\w[^>]+?)(?=<)

corrisponda tutte le parole tra i tag

+0

'(? =>)' Non corrisponde mai quando il resto corrisponde. '(? <=>)' è quello che vuoi. (Sostituisci '(? =>)?' Con '(? <=>)' o '(? <=[>])') –

0

per togliere il contenuto di dimensioni variabili da anche i tag annidati è possibile utilizzare questa espressione regolare che è in realtà un mini-regolare grammatica per quello. (Nota: la macchina PCRE)

(< =>?) (? (: \ W +) (:? \ S *)) *

Problemi correlati