Come faccio a far corrispondere il testo in HTML che non è all'interno dei tag?


8

data una stringa come questa:

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

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 ;-) 22 feb. 092009-02-22 04:30:22

  0

Questi sono tutti rotti.Prova a evidenziare "foo" in "foo <blafoo> foo blabla foo \ n fooo</foo>" 22 feb. 092009-02-22 04:36:53

  0

Reinventare la ruota è così divertente! 22 feb. 092009-02-22 04:43:42

  0

Ora questo è interessante, una risposta accettata con -3 voti ... Avrei dovuto cancellarlo :-( 22 feb. 092009-02-22 05:20:30

  0

@Vlad: Grazie per il test case - ma ancora una volta, ho generato l'HTML da solo. un piccolo numero di possibili forme, e non è una di queste.Tuttavia, ho aggiornato la regex per gestire il tuo caso di test 22 feb. 092009-02-22 05:37:50

  0

@raldi: I corretti 22 feb. 092009-02-22 06:05:02

  0

Votato: è una cazzata per votare qualcuno per aver provato a rispondi alla domanda dell'OP come vuole l'OP Sì, potresti pensare di reinventare la ruota e tutti sanno che non puoi scrivere un parser HTML completo e corretto con un'espressione regolare, ma l'OP vuole ciò che vuole (e ha delle ragioni). Non ha senso dare un calcio a David. 22 feb. 092009-02-22 12:29:53

  0

Grazie per il supporto ;-) (ti ho dato un upvot casuale per quel commento) 22 feb. 092009-02-22 12:54:40

  0

(\ G |>) è sufficiente; pos() viene resettato all'avvio di s /// g. 22 feb. 092009-02-22 18:55:19


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. 22 feb. 092009-02-22 04:17:59

  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.) 22 feb. 092009-02-22 04:24:23

  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 . 22 feb. 092009-02-22 04:42:36

  0

Genero l'HTML da solo. Non ha commenti o sezioni CDATA. Lo script è di 25 righe. Non ho intenzione di aggiungere una dipendenza a un file esterno - quello che stai proponendo è la definizione di overengineering. 22 feb. 092009-02-22 05:26:59

  0

Ma vedi, ho già fatto l'ingegneria per te. Riutilizzare ... ne hai sentito parlare? 22 feb. 092009-02-22 05:30:13

  0

Bloat: ne hai mai sentito parlare? 22 feb. 092009-02-22 05:32:18

  0

Stupido avanti e indietro: ne avete mai sentito parlare? 22 feb. 092009-02-22 05:34:40

  0

http://xkcd.com/386/ :) 22 feb. 092009-02-22 05:43:13

  0

È vero, quel fumetto è jrockway, ma tu sei sbagliato. 22 feb. 092009-02-22 05:44:40

  0

Ci vuole un sacco di arroganza (e non il buon genere) per presumere che tu sappia di più sulla natura del mio progetto di me. In questo caso, la comodità di poter copiare un singolo file in giro - nessun requisito CPAN - supera di gran lunga i vantaggi di essere in grado di analizzare CDATA e altre forme simili di 22 feb. 092009-02-22 05:58:00

  0

... di HTML complesso che non sarà mai realmente chiesto di analizzare. 22 feb. 092009-02-22 05:58:44

  0

@raldi: due cose. Innanzitutto, il rischio è che anche se si genera l'HTML da soli, le proprie esigenze potrebbero cambiare un giorno. L'utilizzo di una soluzione predefinita facilita il compito di farlo funzionare di fronte a requisiti in evoluzione. Naturalmente, solo tu puoi giudicare se sia ragionevole qui. 22 feb. 092009-02-22 15:21:07

  0

@raldi: in secondo luogo, anche se questa non è la soluzione migliore per te, è positivo che questa soluzione sia qui in modo che se qualcun altro con un problema simile (ma non lo stesso) trovi la tua domanda qui, la risposta di jrockway potrebbe funzionare quando quello per i vostri requisiti esatti non lo farà. 22 feb. 092009-02-22 15:22:06

  0

Esattamente. Sto spiegando come farlo genericamente. Se si desidera l'aiuto specifico per le proprie esigenze, si applica la normale tariffa di consulenza :) 22 feb. 092009-02-22 16:39:19

  0

@Adam: Se le mie esigenze cambiano un giorno e si è verificato un errore grave, è possibile annullarlo rimuovendo una singola riga di codice. Non è una grande preoccupazione qui. 24 feb. 092009-02-24 03:03:45


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 '(? <=[>])') 19 ott. 122012-10-19 21:59:45


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 *)) *