Cosa fare Il modello di espressione regolare non corrisponde in nessun punto della stringa?


182

Sto cercando di abbinare <input>i campi "nascosti" di tipo usando questo schema:

/<input type="hidden" name="([^"]*?)" value="([^"]*?)" />/

Questi sono i dati del modulo di esempio:

<input type="hidden" name="SaveRequired" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input type="hidden" name="__VIEWSTATE3" value="ZVVV91yjY" /><input type="hidden" name="__VIEWSTATE0" value="3" /><input type="hidden" name="__VIEWSTATE" value="" /><input type="hidden" name="__VIEWSTATE" value="" />

Ma io non sono sicuro che il type, name, e valuegli attributi appariranno sempre nello stesso ordine. Se l' typeattributo arriva per ultimo, la corrispondenza fallirà perché nel mio modello è all'inizio.

Domanda:
Come posso modificare il mio modello in modo che corrisponda indipendentemente dalle posizioni degli attributi nel <input>tag?

PS: A proposito, sto usando lo strumento desktop RegEx basato su Adobe Air per testare le espressioni regolari.


4
regex è una soluzione eccellente se hai il controllo su HTML generato perché è un dibattito regolare contro non regolare, ma nel mio caso non so come cambierà HTML in futuro, quindi la cosa migliore è usare parser invece di regex, ho usato regex nel mio progetto in parti su cui ho il controllo
Salman,

Risposte:


108

Contrariamente a tutte le risposte qui, per quello che stai cercando di fare regex è una soluzione perfettamente valida. Questo perché NON stai cercando di abbinare i tag bilanciati, sarebbe impossibile con regex! Ma stai abbinando solo ciò che è in un tag, ed è perfettamente regolare.

Ecco il problema, però. Non puoi farlo con una sola regex ... devi fare una partita per catturare un <input>tag, quindi fare un'ulteriore elaborazione su quello. Nota che questo funzionerà solo se nessuno dei valori degli attributi ha un >carattere, quindi non è perfetto, ma dovrebbe essere sufficiente per input sani.

Ecco un po 'di codice Perl (pseudo) per mostrarti cosa intendo:

my $html = readLargeInputFile();

my @input_tags = $html =~ m/
    (
        <input                      # Starts with "<input"
        (?=[^>]*?type="hidden")     # Use lookahead to make sure that type="hidden"
        [^>]+                       # Grab the rest of the tag...
        \/>                         # ...except for the />, which is grabbed here
    )/xgm;

# Now each member of @input_tags is something like <input type="hidden" name="SaveRequired" value="False" />

foreach my $input_tag (@input_tags)
{
  my $hash_ref = {};
  # Now extract each of the fields one at a time.

  ($hash_ref->{"name"}) = $input_tag =~ /name="([^"]*)"/;
  ($hash_ref->{"value"}) = $input_tag =~ /value="([^"]*)"/;

  # Put $hash_ref in a list or something, or otherwise process it
}

Il principio di base qui è, non cercare di fare troppo con un'espressione regolare. Come hai notato, le espressioni regolari impongono un certo ordine. Quindi quello che devi fare invece è prima di tutto abbinare il CONTESTO di ciò che stai cercando di estrarre, quindi eseguire l'inoltro sui dati che desideri.

EDIT: Tuttavia, concordo sul fatto che, in generale, l'utilizzo di un parser HTML è probabilmente più facile e migliore e dovresti davvero considerare di ridisegnare il tuo codice o riesaminare i tuoi obiettivi. :-) Ma ho dovuto pubblicare questa risposta come un contrappeso alla reazione istintiva secondo cui l'analisi di qualsiasi sottoinsieme di HTML è impossibile: HTML e XML sono entrambi irregolari quando si considera l'intera specifica, ma la specifica di un tag è abbastanza regolare , certamente sotto il potere di PCRE.


14
Non è contrario a tutte le risposte qui. :)
dal

6
@tchrist: La tua risposta non era qui quando ho pubblicato la mia. ;-)
Platinum Azure

7
yah bene - per qualche motivo mi ci è voluto più tempo per scrivere di quanto non abbia fatto il tuo. Penso che la mia tastiera debba essere ingrassata. :)
tchrist,

6
È un codice HTML non valido - dovrebbe essere value = "& lt; Ne sei davvero sicuro? & Gt;" Se il posto che sta raschiando fa un lavoro scadente sfuggendo a cose del genere, allora avrà bisogno di una soluzione più sofisticata - ma se lo fanno nel modo giusto (e se ha il controllo su di esso, dovrebbe assicurarsi che sia giusto), allora sta bene.
Ross Snyder l'

14
Link obbligatorio alla migliore risposta SO in materia (possibilmente miglior periodo risposta SO): stackoverflow.com/questions/1732348/…
Daniel Ribeiro

682

Oh sì, puoi usare Regexes per analizzare HTML!

Per l'attività che stai tentando, le regex vanno benissimo!

Si è vero che la maggior parte delle persone sottovaluta la difficoltà di parsing del codice HTML con le espressioni regolari, e quindi lo fanno male.

Ma questo non è un difetto fondamentale legato alla teoria computazionale. Quella stupidità è molto pappagallo da queste parti , ma non ci credi.

Quindi, anche se certamente può essere fatto (questo post serve come prova dell'esistenza di questo fatto incontrovertibile), ciò non significa che  dovrebbe  essere.

Devi decidere tu stesso se sei pronto a scrivere ciò che equivale a un parser HTML dedicato e speciale da regex. Molte persone non lo sono.

Ma lo sono. ☻


Soluzioni generali di analisi HTML basate su Regex

Per prima cosa mostrerò quanto è facile analizzare arbitrariamente HTML con regex. Il programma completo è alla fine di questo post, ma il cuore del parser è:

for (;;) {
  given ($html) {
    last                    when (pos || 0) >= length;
    printf "\@%d=",              (pos || 0);
    print  "doctype "   when / \G (?&doctype)  $RX_SUBS  /xgc;
    print  "cdata "     when / \G (?&cdata)    $RX_SUBS  /xgc;
    print  "xml "       when / \G (?&xml)      $RX_SUBS  /xgc;
    print  "xhook "     when / \G (?&xhook)    $RX_SUBS  /xgc;
    print  "script "    when / \G (?&script)   $RX_SUBS  /xgc;
    print  "style "     when / \G (?&style)    $RX_SUBS  /xgc;
    print  "comment "   when / \G (?&comment)  $RX_SUBS  /xgc;
    print  "tag "       when / \G (?&tag)      $RX_SUBS  /xgc;
    print  "untag "     when / \G (?&untag)    $RX_SUBS  /xgc;
    print  "nasty "     when / \G (?&nasty)    $RX_SUBS  /xgc;
    print  "text "      when / \G (?&nontag)   $RX_SUBS  /xgc;
    default {
      die "UNCLASSIFIED: " .
        substr($_, pos || 0, (length > 65) ? 65 : length);
    }
  }
}

Vedi come facile da leggere?

Come scritto, identifica ogni pezzo di HTML e dice dove ha trovato quel pezzo. Puoi facilmente modificarlo per fare qualsiasi altra cosa tu voglia con un dato tipo di pezzo, o per tipi più particolari di questi.

Non ho casi di test non riusciti (a sinistra :): ho eseguito con successo questo codice su oltre 100.000 file HTML, ognuno dei quali ho potuto mettere le mani rapidamente e facilmente. Oltre a questi, l'ho anche eseguito su file appositamente creati per rompere parser ingenui.

Questo non lo è un parser ingenuo.

Oh, sono sicuro che non sia perfetto, ma non sono ancora riuscito a romperlo. Immagino che anche se qualcosa lo facesse, la correzione sarebbe facile da inserire a causa della struttura chiara del programma. Anche i programmi pesanti di regex dovrebbero avere una struttura.

Ora che è fuori mano, lasciami rispondere alla domanda del PO.

Dimostrazione di risolvere il compito del PO utilizzando Regexes

Il piccolo html_input_rxprogramma che includo di seguito produce il seguente output, in modo da poter vedere che l'analisi dell'HTML con regex funziona perfettamente per quello che desideri fare:

% html_input_rx Amazon.com-_Online_Shopping_for_Electronics,_Apparel,_Computers,_Books,_DVDs_\&_more.htm 
input tag #1 at character 9955:
       class => "searchSelect"
          id => "twotabsearchtextbox"
        name => "field-keywords"
        size => "50"
       style => "width:100%; background-color: #FFF;"
       title => "Search for"
        type => "text"
       value => ""

input tag #2 at character 10335:
         alt => "Go"
         src => "http://g-ecx.images-amazon.com/images/G/01/x-locale/common/transparent-pixel._V192234675_.gif"
        type => "image"

Tag di input di analisi, vedere Nessun input male

Ecco la fonte per il programma che ha prodotto l'output sopra.

#!/usr/bin/env perl
#
# html_input_rx - pull out all <input> tags from (X)HTML src
#                  via simple regex processing
#
# Tom Christiansen <tchrist@perl.com>
# Sat Nov 20 10:17:31 MST 2010
#
################################################################

use 5.012;

use strict;
use autodie;
use warnings FATAL => "all";    
use subs qw{
    see_no_evil
    parse_input_tags
    input descape dequote
    load_patterns
};    
use open        ":std",
          IN => ":bytes",
         OUT => ":utf8";    
use Encode qw< encode decode >;

    ###########################################################

                        parse_input_tags 
                           see_no_evil 
                              input  

    ###########################################################

until eof(); sub parse_input_tags {
    my $_ = shift();
    our($Input_Tag_Rx, $Pull_Attr_Rx);
    my $count = 0;
    while (/$Input_Tag_Rx/pig) {
        my $input_tag = $+{TAG};
        my $place     = pos() - length ${^MATCH};
        printf "input tag #%d at character %d:\n", ++$count, $place;
        my %attr = ();
        while ($input_tag =~ /$Pull_Attr_Rx/g) {
            my ($name, $value) = @+{ qw< NAME VALUE > };
            $value = dequote($value);
            if (exists $attr{$name}) {
                printf "Discarding dup attr value '%s' on %s attr\n",
                    $attr{$name} // "<undef>", $name;
            } 
            $attr{$name} = $value;
        } 
        for my $name (sort keys %attr) {
            printf "  %10s => ", $name;
            my $value = descape $attr{$name};
            my  @Q; given ($value) {
                @Q = qw[  " "  ]  when !/'/ && !/"/;
                @Q = qw[  " "  ]  when  /'/ && !/"/;
                @Q = qw[  ' '  ]  when !/'/ &&  /"/;
                @Q = qw[ q( )  ]  when  /'/ &&  /"/;
                default { die "NOTREACHED" }
            } 
            say $Q[0], $value, $Q[1];
        } 
        print "\n";
    } 

}

sub dequote {
    my $_ = $_[0];
    s{
        (?<quote>   ["']      )
        (?<BODY>    
          (?s: (?! \k<quote> ) . ) * 
        )
        \k<quote> 
    }{$+{BODY}}six;
    return $_;
} 

sub descape {
    my $string = $_[0];
    for my $_ ($string) {
        s{
            (?<! % )
            % ( \p{Hex_Digit} {2} )
        }{
            chr hex $1;
        }gsex;
        s{
            & \043 
            ( [0-9]+ )
            (?: ; 
              | (?= [^0-9] )
            )
        }{
            chr     $1;
        }gsex;
        s{
            & \043 x
            ( \p{ASCII_HexDigit} + )
            (?: ; 
              | (?= \P{ASCII_HexDigit} )
            )
        }{
            chr hex $1;
        }gsex;

    }
    return $string;
} 

sub input { 
    our ($RX_SUBS, $Meta_Tag_Rx);
    my $_ = do { local $/; <> };  
    my $encoding = "iso-8859-1";  # web default; wish we had the HTTP headers :(
    while (/$Meta_Tag_Rx/gi) {
        my $meta = $+{META};
        next unless $meta =~ m{             $RX_SUBS
            (?= http-equiv ) 
            (?&name) 
            (?&equals) 
            (?= (?&quote)? content-type )
            (?&value)    
        }six;
        next unless $meta =~ m{             $RX_SUBS
            (?= content ) (?&name) 
                          (?&equals) 
            (?<CONTENT>   (?&value)    )
        }six;
        next unless $+{CONTENT} =~ m{       $RX_SUBS
            (?= charset ) (?&name) 
                          (?&equals) 
            (?<CHARSET>   (?&value)    )
        }six;
        if (lc $encoding ne lc $+{CHARSET}) {
            say "[RESETTING ENCODING $encoding => $+{CHARSET}]";
            $encoding = $+{CHARSET};
        }
    } 
    return decode($encoding, $_);
}

sub see_no_evil {
    my $_ = shift();

    s{ <!    DOCTYPE  .*?         > }{}sx; 
    s{ <! \[ CDATA \[ .*?    \]\] > }{}gsx; 

    s{ <script> .*?  </script> }{}gsix; 
    s{ <!--     .*?        --> }{}gsx;

    return $_;
}

sub load_patterns { 

    our $RX_SUBS = qr{ (?(DEFINE)
        (?<nv_pair>         (?&name) (?&equals) (?&value)         ) 
        (?<name>            \b (?=  \pL ) [\w\-] + (?<= \pL ) \b  )
        (?<equals>          (?&might_white)  = (?&might_white)    )
        (?<value>           (?&quoted_value) | (?&unquoted_value) )
        (?<unwhite_chunk>   (?: (?! > ) \S ) +                    )
        (?<unquoted_value>  [\w\-] *                              )
        (?<might_white>     \s *                                  )
        (?<quoted_value>
            (?<quote>   ["']      )
            (?: (?! \k<quote> ) . ) *
            \k<quote> 
        )
        (?<start_tag>  < (?&might_white) )
        (?<end_tag>          
            (?&might_white)
            (?: (?&html_end_tag) 
              | (?&xhtml_end_tag) 
             )
        )
        (?<html_end_tag>       >  )
        (?<xhtml_end_tag>    / >  )
    ) }six; 

    our $Meta_Tag_Rx = qr{                          $RX_SUBS 
        (?<META> 
            (?&start_tag) meta \b
            (?:
                (?&might_white) (?&nv_pair) 
            ) +
            (?&end_tag)
        )
    }six;

    our $Pull_Attr_Rx = qr{                         $RX_SUBS
        (?<NAME>  (?&name)      )
                  (?&equals) 
        (?<VALUE> (?&value)     )
    }six;

    our $Input_Tag_Rx = qr{                         $RX_SUBS 

        (?<TAG> (?&input_tag) )

        (?(DEFINE)

            (?<input_tag>
                (?&start_tag)
                input
                (?&might_white) 
                (?&attributes) 
                (?&might_white) 
                (?&end_tag)
            )

            (?<attributes>
                (?: 
                    (?&might_white) 
                    (?&one_attribute) 
                ) *
            )

            (?<one_attribute>
                \b
                (?&legal_attribute)
                (?&might_white) = (?&might_white) 
                (?:
                    (?&quoted_value)
                  | (?&unquoted_value)
                )
            )

            (?<legal_attribute> 
                (?: (?&optional_attribute)
                  | (?&standard_attribute)
                  | (?&event_attribute)
            # for LEGAL parse only, comment out next line 
                  | (?&illegal_attribute)
                )
            )

            (?<illegal_attribute>  (?&name) )

            (?<required_attribute> (?#no required attributes) )

            (?<optional_attribute>
                (?&permitted_attribute)
              | (?&deprecated_attribute)
            )

            # NB: The white space in string literals 
            #     below DOES NOT COUNT!   It's just 
            #     there for legibility.

            (?<permitted_attribute>
                  accept
                | alt
                | bottom
                | check box
                | checked
                | disabled
                | file
                | hidden
                | image
                | max length
                | middle
                | name
                | password
                | radio
                | read only
                | reset
                | right
                | size
                | src
                | submit
                | text
                | top
                | type
                | value
            )

            (?<deprecated_attribute>
                  align
            )

            (?<standard_attribute>
                  access key
                | class
                | dir
                | ltr
                | id
                | lang
                | style
                | tab index
                | title
                | xml:lang
            )

            (?<event_attribute>
                  on blur
                | on change
                | on click
                | on dbl   click
                | on focus
                | on mouse down
                | on mouse move
                | on mouse out
                | on mouse over
                | on mouse up
                | on key   down
                | on key   press
                | on key   up
                | on select
            )
        )
    }six;

}

UNITCHECK {
    load_patterns();
} 

END {
    close(STDOUT) 
        || die "can't close stdout: $!";
} 

Ecco qua! Niente da fare! :)

Solo tu puoi giudicare se la tua abilità con le regex dipende da un particolare compito di analisi. Il livello di abilità di ognuno è diverso e ogni nuova attività è diversa. Per i lavori in cui hai un set di input ben definito, le regex sono ovviamente la scelta giusta, perché è banale metterne insieme alcuni quando hai un sottoinsieme limitato di HTML da affrontare. Anche i principianti di regex dovrebbero essere in grado di gestire quei lavori con regex. Qualcos'altro è eccessivo.

Tuttavia , una volta che l'HTML inizia a essere meno inchiodato, una volta che inizia a ramificarsi in modi che non puoi prevedere ma che sono perfettamente legali, una volta che devi abbinare più tipi diversi di cose o con dipendenze più complesse, alla fine raggiungerai un punto in cui devi lavorare di più per effettuare una soluzione che utilizza regex di quanto non dovresti usare una classe di analisi. Dove cade quel punto di pareggio dipende di nuovo dal tuo livello di comfort con le regex.

Quindi cosa dovrei fare?

Non ti dirò cosa devi fare o cosa non puoi fare. Penso che sia sbagliato. Voglio solo presentarti delle possibilità, aprire un po 'gli occhi. Puoi scegliere cosa vuoi fare e come vuoi farlo. Non ci sono assoluti - e nessun altro conosce la tua situazione come tu stesso. Se qualcosa sembra troppo lavoro, beh, forse lo è. La programmazione dovrebbe essere divertente , lo sai. Altrimenti, potresti sbagliare.

Uno può guardare il mio html_input_rxprogramma in qualsiasi numero di modi validi. Uno di questi è che, invece, si può analizzare HTML con le espressioni regolari. Ma un altro è che è molto, molto, molto più difficile di quanto quasi nessuno pensi mai. Questo può facilmente portare alla conclusione che il mio programma è una testimonianza di ciò che non dovresti fare, perché è davvero troppo difficile.

Non sarò d'accordo con quello. Certamente se tutto ciò che faccio nel mio programma non ha senso per te dopo qualche studio, non dovresti tentare di usare regex per questo tipo di attività. Per HTML specifico, le regex sono fantastiche, ma per HTML generico, equivalgono alla follia. Uso sempre le classi di analisi, specialmente se è HTML non mi sono generato.

Regexes ottimale per piccoli problemi di analisi HTML, pessimale per quelli di grandi dimensioni

Anche se il mio programma è presa come esempio del perché si dovrebbe non usare espressioni regolari per l'analisi generale HTML - che è OK, perché ho un pò intendevo per essere che ☺ - è ancora dovrebbe essere una rivelazione così che più persone rompere il terribilmente comuni e cattiva, cattiva abitudine di scrivere schemi illeggibili, non strutturati e non mantenibili.

I modelli non devono essere brutti e non devono essere duri. Se crei brutti schemi, è una riflessione su di te, non su di loro.

Fenomenale linguaggio regex squisito

Mi è stato chiesto di sottolineare che la mia proficua soluzione al tuo problema è stata scritta in Perl. Sei sorpreso? Non l'hai notato? Questa rivelazione è una bomba?

È vero che non tutti gli altri strumenti e linguaggi di programmazione sono altrettanto convenienti, espressivi e potenti quando si tratta di regex come Perl. C'è un grande spettro là fuori, con alcuni più adatti di altri. In generale, le lingue che hanno espresso regex come parte del linguaggio principale anziché come libreria sono più facili da lavorare. Non ho fatto nulla con regex in cui non potevi fare, diciamo, PCRE, anche se struttureresti il ​​programma in modo diverso se stessi usando C.

Alla fine, altre lingue saranno al passo con il momento in cui Perl si trova in termini di regex. Dico questo perché quando Perl ha iniziato, nessun altro aveva qualcosa di simile alle regex di Perl. Di 'tutto quello che ti piace, ma è qui che Perl ha vinto chiaramente: tutti hanno copiato le regex di Perl, sebbene in varie fasi del loro sviluppo. Perl è stato il pioniere di quasi (non del tutto, ma quasi) tutto ciò su cui sei arrivato a fare affidamento oggi nei modelli moderni, indipendentemente dallo strumento o dalla lingua che usi. Quindi alla fine gli altri li raggiungeranno.

Ma raggiungeranno solo dove Perl era in passato, proprio come è ora. Tutto avanza. Nelle regex se non altro, dove conduce Perl, altri seguono. Dove sarà Perl una volta che tutti gli altri finalmente raggiungeranno il punto in cui si trova Perl adesso? Non ne ho idea, ma so che anche noi ci saremo trasferiti. Probabilmente saremo più vicini allo stile di creazione di modelli di Perl .

Se ti piace quel genere di cose ma ti piacerebbe usarlo in Perl₅, potresti essere interessato al meraviglioso modulo Regexp :: Grammars di Damian Conway . È completamente fantastico e fa sembrare quello che ho fatto qui nel mio programma tanto primitivo quanto il mio, rendendo i modelli che le persone si raggruppano senza spazi bianchi o identificatori alfabetici. Controlla!


Chunker HTML semplice

Ecco la fonte completa del parser da cui ho mostrato il centrotavola all'inizio di questo post.

Io non suggerendo che si dovrebbe usare questo su una classe di analisi rigorosamente testati. Ma sono stanco delle persone che fingono che nessuno possa analizzare l'HTML con regex solo perché non possono. È chiaramente possibile e questo programma è la prova di tale affermazione.

Certo, non è facile, ma si è possibile!

E provare a farlo è una terribile perdita di tempo, perché esistono buone classi di analisi che dovresti usare per questo compito. La risposta giusta alle persone che cercano di analizzare HTML arbitrario non è impossibile. Questa è una risposta facile e disingenua. La risposta corretta e onesta è che non dovrebbero tentare perché è troppo fastidioso capire da zero; non dovrebbero spezzarsi la schiena cercando di reinventare una ruota che funziona perfettamente bene.

D'altra parte, l'HTML che rientra in un sottoinsieme prevedibile è estremamente facile da analizzare con regex. Non sorprende che le persone provino ad usarli, perché per piccoli problemi, forse per i giocattoli, niente potrebbe essere più facile. Ecco perché è così importante distinguere i due compiti - specifico e generico - in quanto questi non richiedono necessariamente lo stesso approccio.

Spero in futuro qui di vedere un trattamento più equo e onesto delle domande su HTML e regex.

Ecco il mio lexer HTML. Non tenta di eseguire un'analisi di convalida; identifica solo gli elementi lessicali. Potresti pensarlo più come un grosso pezzo di HTML che un parser HTML. Non perdona molto l'HTML non funzionante, anche se fa delle ridottissime riduzioni in quella direzione.

Anche se non analizzi mai l'HTML completo da solo (e perché dovresti? È un problema risolto!), Questo programma ha molti bit regex interessanti che credo che molte persone possano imparare molto. Godere!

#!/usr/bin/env perl
#
# chunk_HTML - a regex-based HTML chunker
#
# Tom Christiansen <tchrist@perl.com
#   Sun Nov 21 19:16:02 MST 2010
########################################

use 5.012;

use strict;
use autodie;
use warnings qw< FATAL all >;
use open     qw< IN :bytes OUT :utf8 :std >;

MAIN: {
  $| = 1;
  lex_html(my $page = slurpy());
  exit();
}

########################################################################
sub lex_html {
    our $RX_SUBS;                                        ###############
    my  $html = shift();                                 # Am I...     #
    for (;;) {                                           # forgiven? :)#
        given ($html) {                                  ###############
            last                when (pos || 0) >= length;
            printf "\@%d=",          (pos || 0);
            print  "doctype "   when / \G (?&doctype)  $RX_SUBS  /xgc;
            print  "cdata "     when / \G (?&cdata)    $RX_SUBS  /xgc;
            print  "xml "       when / \G (?&xml)      $RX_SUBS  /xgc;
            print  "xhook "     when / \G (?&xhook)    $RX_SUBS  /xgc;
            print  "script "    when / \G (?&script)   $RX_SUBS  /xgc;
            print  "style "     when / \G (?&style)    $RX_SUBS  /xgc;
            print  "comment "   when / \G (?&comment)  $RX_SUBS  /xgc;
            print  "tag "       when / \G (?&tag)      $RX_SUBS  /xgc;
            print  "untag "     when / \G (?&untag)    $RX_SUBS  /xgc;
            print  "nasty "     when / \G (?&nasty)    $RX_SUBS  /xgc;
            print  "text "      when / \G (?&nontag)   $RX_SUBS  /xgc;
            default {
                die "UNCLASSIFIED: " .
                  substr($_, pos || 0, (length > 65) ? 65 : length);
            }
        }
    }
    say ".";
}
#####################
# Return correctly decoded contents of next complete
# file slurped in from the <ARGV> stream.
#
sub slurpy {
    our ($RX_SUBS, $Meta_Tag_Rx);
    my $_ = do { local $/; <ARGV> };   # read all input

    return unless length;

    use Encode   qw< decode >;

    my $bom = "";
    given ($_) {
        $bom = "UTF-32LE" when / ^ \xFf \xFe \0   \0   /x;  # LE
        $bom = "UTF-32BE" when / ^ \0   \0   \xFe \xFf /x;  #   BE
        $bom = "UTF-16LE" when / ^ \xFf \xFe           /x;  # le
        $bom = "UTF-16BE" when / ^ \xFe \xFf           /x;  #   be
        $bom = "UTF-8"    when / ^ \xEF \xBB \xBF      /x;  # st00pid
    }
    if ($bom) {
        say "[BOM $bom]";
        s/^...// if $bom eq "UTF-8";                        # st00pid

        # Must use UTF-(16|32) w/o -[BL]E to strip BOM.
        $bom =~ s/-[LB]E//;

        return decode($bom, $_);

        # if BOM found, don't fall through to look
        #  for embedded encoding spec
    }

    # Latin1 is web default if not otherwise specified.
    # No way to do this correctly if it was overridden
    # in the HTTP header, since we assume stream contains
    # HTML only, not also the HTTP header.
    my $encoding = "iso-8859-1";
    while (/ (?&xml) $RX_SUBS /pgx) {
        my $xml = ${^MATCH};
        next unless $xml =~ m{              $RX_SUBS
            (?= encoding )  (?&name)
                            (?&equals)
                            (?&quote) ?
            (?<ENCODING>    (?&value)       )
        }sx;
        if (lc $encoding ne lc $+{ENCODING}) {
            say "[XML ENCODING $encoding => $+{ENCODING}]";
            $encoding = $+{ENCODING};
        }
    }

    while (/$Meta_Tag_Rx/gi) {
        my $meta = $+{META};

        next unless $meta =~ m{             $RX_SUBS
            (?= http-equiv )    (?&name)
                                (?&equals)
            (?= (?&quote)? content-type )
                                (?&value)
        }six;

        next unless $meta =~ m{             $RX_SUBS
            (?= content )       (?&name)
                                (?&equals)
            (?<CONTENT>         (?&value)    )
        }six;

        next unless $+{CONTENT} =~ m{       $RX_SUBS
            (?= charset )       (?&name)
                                (?&equals)
            (?<CHARSET>         (?&value)    )
        }six;

        if (lc $encoding ne lc $+{CHARSET}) {
            say "[HTTP-EQUIV ENCODING $encoding => $+{CHARSET}]";
            $encoding = $+{CHARSET};
        }
    }

    return decode($encoding, $_);
}
########################################################################
# Make sure to this function is called
# as soon as source unit has been compiled.
UNITCHECK { load_rxsubs() }

# useful regex subroutines for HTML parsing
sub load_rxsubs {

    our $RX_SUBS = qr{
      (?(DEFINE)

        (?<WS> \s *  )

        (?<any_nv_pair>     (?&name) (?&equals) (?&value)         )
        (?<name>            \b (?=  \pL ) [\w:\-] +  \b           )
        (?<equals>          (?&WS)  = (?&WS)    )
        (?<value>           (?&quoted_value) | (?&unquoted_value) )
        (?<unwhite_chunk>   (?: (?! > ) \S ) +                    )

        (?<unquoted_value>  [\w:\-] *                             )

        (?<any_quote>  ["']      )

        (?<quoted_value>
            (?<quote>   (?&any_quote)  )
            (?: (?! \k<quote> ) . ) *
            \k<quote>
        )

        (?<start_tag>       < (?&WS)      )
        (?<html_end_tag>      >           )
        (?<xhtml_end_tag>   / >           )
        (?<end_tag>
            (?&WS)
            (?: (?&html_end_tag)
              | (?&xhtml_end_tag) )
         )

        (?<tag>
            (?&start_tag)
            (?&name)
            (?:
                (?&WS)
                (?&any_nv_pair)
            ) *
            (?&end_tag)
        )

        (?<untag> </ (?&name) > )

        # starts like a tag, but has screwed up quotes inside it
        (?<nasty>
            (?&start_tag)
            (?&name)
            .*?
            (?&end_tag)
        )

        (?<nontag>    [^<] +            )

        (?<string> (?&quoted_value)     )
        (?<word>   (?&name)             )

        (?<doctype>
            <!DOCTYPE
                # please don't feed me nonHTML
                ### (?&WS) HTML
            [^>]* >
        )

        (?<cdata>   <!\[CDATA\[     .*?     \]\]    > )
        (?<script>  (?= <script ) (?&tag)   .*?     </script> )
        (?<style>   (?= <style  ) (?&tag)   .*?     </style> )
        (?<comment> <!--            .*?           --> )

        (?<xml>
            < \? xml
            (?:
                (?&WS)
                (?&any_nv_pair)
            ) *
            (?&WS)
            \? >
        )

        (?<xhook> < \? .*? \? > )

      )

    }six;

    our $Meta_Tag_Rx = qr{                          $RX_SUBS
        (?<META>
            (?&start_tag) meta \b
            (?:
                (?&WS) (?&any_nv_pair)
            ) +
            (?&end_tag)
        )
    }six;

}

# nobody *ever* remembers to do this!
END { close STDOUT }

23
due punti salienti del tuo commento "Uso sempre le classi di analisi, soprattutto se si tratta di HTML che non mi sono generato". e "I modelli non devono essere brutti e non devono essere duri. Se crei modelli brutti, è una riflessione su di te, non su di loro." sono totalmente d'accordo con quello che hai detto, quindi sto rivalutando il problema. grazie mille per una risposta così dettagliata
Salman,

168
Per chi non lo sapesse, ho pensato di menzionare che Tom è il coautore di "Programming Perl" (alias il libro Camel) e una delle massime autorità del Perl. Se dubiti che questo sia il vero Tom Christiansen, torna indietro e leggi il post.
Bill Ruppert,

20
Per riassumere: i RegEx sono sbagliati. Penso che sia un peccato, ma non cambierà. I motori "RegEx" compatibili non possono rifiutare lingue non regolari. Pertanto non possono essere implementati correttamente solo con macchine a stati finiti. I concetti potenti attorno alle classi di calcolo non si applicano. L'uso di RegEx non garantisce i tempi di esecuzione O (n). I vantaggi di RegEx sono la sintassi concisa e il dominio implicito del riconoscimento dei caratteri. Per me, questo è un disastro ferroviario che si muove lentamente, impossibile da distogliere lo sguardo, ma con conseguenze orribili che si svolgono.
Steve Steiner,

27
@tchrist, questo non risponde mai alla domanda originale dei PO. E sta analizzando il termine corretto qui? Afaics che il regex sta facendo analisi tokenizzante / lessicale, ma l'analisi finale è stata eseguita con il codice Perl, non il regex stesso.
Qtax,

65
@tchrist Molto impressionante. Ovviamente sei un programmatore Perl altamente qualificato e di talento, ed estremamente informato sulle moderne espressioni regolari. Vorrei sottolineare, tuttavia, che ciò che hai scritto non è in realtà un'espressione regolare (moderna, regolare o di altro tipo), ma piuttosto un programma Perl che utilizza fortemente le espressioni regolari. Il tuo post supporta davvero l'affermazione secondo cui le espressioni regolari possono analizzare correttamente l'HTML? O è più simile alla prova che Perl può analizzare correttamente l'HTML? Ad ogni modo, bel lavoro!
Mike Clark

126
  1. Puoi scrivere un romanzo come ha fatto Tchrist
  2. Puoi usare una libreria DOM, caricare l'HTML e usare xpath e semplicemente usare //input[@type="hidden"]. O se non vuoi usare xpath, prendi semplicemente tutti gli input e filtra con quali sono nascosti getAttribute.

Preferisco il n. 2.

<?php

$d = new DOMDocument();
$d->loadHTML(
    '
    <p>fsdjl</p>
    <form><div>fdsjl</div></form>
    <input type="hidden" name="blah" value="hide yo kids">
    <input type="text" name="blah" value="hide yo kids">
    <input type="hidden" name="blah" value="hide yo wife">
');
$x = new DOMXpath($d);
$inputs = $x->evaluate('//input[@type="hidden"]');

foreach ( $inputs as $input ) {
    echo $input->getAttribute('value'), '<br>';
}

Risultato:

hide yo kids<br>hide yo wife<br>

72
Questo è stato un po 'il mio punto, in realtà. Volevo mostrare quanto sia difficile.
tchrist,

19
Roba molto buona lì. Avevo davvero sperato che le persone mostrassero quanto è più facile usare una lezione di analisi, quindi grazie! Volevo solo un esempio funzionante dell'estrema difficoltà che devi affrontare per farlo da zero usando regex. Spero davvero che la maggior parte delle persone concluda di usare parser prefabbricati su HTML generico invece di lanciare il proprio. I regex sono comunque ottimi per il semplice HTML che si sono fatti da soli, perché questo elimina il 99,98% della complessità.
tchrist,

5
Ciò che sarebbe bello dopo aver letto quei 2 approcci molto interessanti sarebbe il confronto della velocità / utilizzo della memoria / CPU di un approccio con un altro (cioè classe di analisi VS basata su regex).
the_yellow_logo

1
@ Avt'W Sì, non che dovresti andare a scrivere un 'romanzo' se Regexes dovesse essere più veloce, ma in realtà sarebbe davvero interessante saperlo. :) Ma la mia ipotesi è già, che un parser richiede anche meno risorse ..
Dennis98

Questo è in realtà il motivo per cui XPath è stato inventato in primo luogo!
Thorbjørn Ravn Andersen,

21

Nello spirito della soluzione lexer di Tom Christiansen, ecco un link all'articolo apparentemente dimenticato di Robert Cameron del 1998, REX: XML Shallow Parsing with Regular Expressions.

http://www.cs.sfu.ca/~cameron/REX.html

Astratto

La sintassi di XML è abbastanza semplice da poter analizzare un documento XML in un elenco dei suoi elementi di markup e di testo usando un'unica espressione regolare. Un'analisi così superficiale di un documento XML può essere molto utile per la costruzione di una varietà di strumenti di elaborazione XML leggeri. Tuttavia, espressioni regolari complesse possono essere difficili da costruire e persino più difficili da leggere. Utilizzando una forma di programmazione alfabetica per le espressioni regolari, questo documento documenta una serie di espressioni di analisi superficiale XML che possono essere utilizzate come base per l'analisi superficiale XML semplice, corretta, efficiente, solida e indipendente dal linguaggio. Vengono anche fornite implementazioni complete di parser superficiali di meno di 50 righe ciascuna in Perl, JavaScript e Lex / Flex.

Se ti piace leggere le espressioni regolari, l'articolo di Cameron è affascinante. La sua scrittura è concisa, approfondita e molto dettagliata. Non ti sta semplicemente mostrando come costruire l'espressione regolare REX, ma anche un approccio per costruire qualsiasi regex complessa da parti più piccole.

Uso l'espressione regolare REX da 10 anni per risolvere il tipo di problema richiesto dal poster iniziale (come posso abbinare questo particolare tag ma non qualche altro tag molto simile?). Ho trovato la regex che ha sviluppato per essere completamente affidabile.

REX è particolarmente utile quando ti concentri sui dettagli lessicali di un documento, ad esempio quando trasformi un tipo di documento di testo (ad esempio testo semplice, XML, SGML, HTML) in un altro, dove il documento potrebbe non essere valido, ben formato, o addirittura analizzabile per gran parte della trasformazione. Ti consente di scegliere come target isole di markup ovunque all'interno di un documento senza disturbare il resto del documento.


7

Sebbene adoro i contenuti del resto di queste risposte, in realtà non hanno risposto alla domanda direttamente o nel modo corretto. Anche la risposta di Platinum era eccessivamente complicata e anche meno efficiente. Quindi sono stato costretto a dirlo.

Sono un grande sostenitore di Regex, se usato correttamente. Ma a causa dello stigma (e delle prestazioni), dichiaro sempre che XML o HTML ben formati dovrebbero usare un parser XML. E prestazioni ancora migliori sarebbero l'analisi delle stringhe, anche se c'è una linea tra la leggibilità se diventa troppo fuori controllo. Tuttavia, questa non è la domanda. La domanda è come abbinare un tag di input di tipo nascosto. La risposta è:

<input[^>]*type="hidden"[^>]*>

A seconda del tuo sapore, l'unica opzione regex che dovresti includere è l'opzione ignorecase.


5
<input type='hidden' name='Oh, <really>?' value='Try a real HTML parser instead.'>
Ilmari Karonen,

4
Il tuo esempio si chiude automaticamente. Dovrebbe terminare con />. Inoltre, mentre le possibilità di avere un >nel campo del nome sono quasi nessuna, è davvero possibile che ci sia un >in un handle di azione. Ad esempio: una chiamata javascript in linea sulla proprietà OnClick. Detto questo, ho un parser XML per quelli, ma ho anche un Regex per quelli in cui il documento che mi è stato dato è troppo incasinato per essere gestito dai parser XML, ma un Regex può farlo. Inoltre, non è questa la domanda. Non ti imbatterai mai in queste situazioni con un input nascosto e la mia risposta è la migliore. Ya, <really>!.
Suamere,

3
/>è un XML-ism; non è richiesto in nessuna versione di HTML, ad eccezione di XHTML (che non ha mai guadagnato molta trazione ed è stato quasi sostituito da HTML5). E hai ragione sul fatto che esiste un sacco di codice HTML disordinato non realmente valido là fuori, ma un buon parser HTML ( non XML) dovrebbe essere in grado di far fronte a gran parte di esso; in caso contrario, molto probabilmente neanche i browser.
Ilmari Karonen,

1
Se l'unica analisi o ricerca di cui hai bisogno è un singolo colpo per restituire una raccolta di campi di input nascosti, questa regex sarebbe perfetta. Usare le classi di documenti XML .NET o fare riferimento a un parser XML / HTML di terze parti solo per chiamare un metodo sarebbe eccessivo quando Regex è integrato. E hai ragione che un sito Web è così incasinato che un buon HTML il parser non è in grado di gestirlo, probabilmente non è nemmeno qualcosa che uno sviluppatore starebbe guardando. Ma la mia azienda distribuisce milioni di pagine al mese concatenate e prese in molti modi in modo tale che a volte (non sempre), Regex sia l'opzione migliore.
Suamere,

1
Unico punto è che non siamo sicuri dell'intera ragione per cui questo sviluppatore vuole questa risposta. Ma è quello che ha chiesto.
Suamere,

3

puoi provare questo:

<[A-Za-z ="/_0-9+]*>

e per un risultato più vicino puoi provare questo:

<[ ]*input[ ]+type="hidden"[ ]*name=[A-Za-z ="_0-9+]*[ ]*[/]*>

puoi testare il tuo schema regex qui http://regexpal.com/

questi patten sono buoni per questo:

<input type="hidden" name="SaveRequired" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input type="hidden" name="__VIEWSTATE3" value="ZVVV91yjY" />

e per ordine casuale di type, namee valuepuoi usare questo:

<[ ]*input[ ]*[A-Za-z ="_0-9+/]*>

o

<[ ]*input[ ]*[A-Za-z ="_0-9+/]*[ ]*[/]>

su questo :

<input  name="SaveRequired" type="hidden" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input  name="__VIEWSTATE3" type="hidden" value="ZVVV91yjY" />

`

a proposito penso che tu voglia qualcosa del genere:

<[ ]*input(([ ]*type="hidden"[ ]*name=[A-Za-z0-9_+"]*[ ]*value=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*type="hidden"[ ]*value=[A-Za-z0-9_+"]*[ ]*name=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*name=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*value=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*value=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*name=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*name=[A-Za-z0-9_+"]*[ ]*value=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*)+)[ ]*/>|<[ ]*input(([ ]*value=[A-Za-z0-9_+"]*[ ]*name=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*)+)[ ]*/>

non è buono ma funziona in alcun modo.

testalo in: http://regexpal.com/



0

supponiamo che il contenuto del tuo html sia archiviato nella stringa html, quindi per nascondere ogni input che contenga tipo puoi usare un'espressione regolare

var regex = /(<input.*?type\s?=\s?["']hidden["'].*?>)/g;
html.match(regex);

la regex sopra trova <inputseguita da un numero qualsiasi di caratteri fino a quando non arrivatype="hidden" o type = 'nascosto' seguito da un numero qualsiasi di caratteri fino a quando non viene visualizzato>

/ g indica l'espressione regolare per trovare ogni sottostringa che corrisponda al modello dato.

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.