2014-12-20 14 views
7

Sto scrivendo una funzione di astrazione che chiederà all'utente una determinata domanda e convaliderà la risposta sulla base di una determinata espressione regolare. La domanda viene ripetuta fino a quando la risposta corrisponde all'espressione regolare della convalida. Tuttavia, voglio anche che il client sia in grado di specificare se la risposta deve corrispondere al caso, in modo sensibile o meno. Quindi, qualcosa di simile:Come si usa una variabile come modificatore regex in perl?

sub ask { 
    my ($prompt, $validationRe, $caseSensitive) = @_; 
    my $modifier = ($caseSensitive) ? "" : "i"; 
    my $ans; 
    my $isValid; 

    do { 
     print $prompt; 
     $ans = <>; 
     chomp($ans); 

     # What I want to do that doesn't work: 
     # $isValid = $ans =~ /$validationRe/$modifier; 

     # What I have to do: 
     $isValid = ($caseSensitive) ? 
      ($ans =~ /$validationRe/) : 
      ($ans =~ /$validationRe/i); 

    } while (!$isValid); 

    return $ans; 
} 

Upshot: esiste un modo per specificare dinamicamente modificatori di un'espressione regolare?

+0

oppure c'è un modo migliore per fare ciò che voglio fare? – livefree75

risposta

11

Upshot: esiste un modo per specificare dinamicamente modificatori di un'espressione regolare?

Da perldoc perlre:

"(? Adlupimsx-imsx)" "(?^Alupimsx)" Uno o più incorporato modello-partita modificatori, per essere acceso (o spento , se preceduto da "-") per il resto del pattern o il resto del gruppo di pattern che lo racchiude (se presente).

Ciò è particolarmente utile per i modelli dinamici, come quelli leggere da un file di configurazione, presa da un argomento, o specificato in una tabella qualche parte. Si consideri il caso in cui alcuni pattern vogliono essere maiuscoli e minuscoli, e alcuni non lo fanno: quelli senza distinzione tra maiuscole e minuscole necessitano semplicemente di per includere "(? I)" nella parte anteriore del modello.

che ti dà qualcosa sulla falsariga di

$isValid = $ans =~ m/(?$modifier)$validationRe/; 

Basta essere sicuri di prendere le precauzioni di sicurezza appropriate quando accetta l'input dell'utente in questo modo.

0

È necessario utilizzare la funzione eval. Sotto codice funzionerà:

sub ask { 
    my ($prompt, $validationRe, $caseSensitive) = @_; 
    my $modifier = ($caseSensitive) ? "" : "i"; 
    my $ans; 
    my $isValid; 

    do { 
     print $prompt; 
     $ans = <>; 
     chomp($ans); 

     # What I want to do that doesn't work: 
     # $isValid = $ans =~ /$validationRe/$modifier; 

     $isValid = eval "$ans =~ /$validationRe/$modifier"; 

     # What I have to do: 
     #$isValid = ($caseSensitive) ? 
     # ($ans =~ /$validationRe/) : 
     # ($ans =~ /$validationRe/i); 

    } while (!$isValid); 

    return $ans; 
} 
+1

Ciao, prima di segnalarlo, qualcuno può spiegare cosa c'è di sbagliato con questo codice ?? 'Test: $ ans =" ​​ABCDEF "; $ modifier = "i"; $ validationRe = "cD"; $ isValid = eval "$ ans = ~/$ validationRe/$ modifier"; stampa $ isValid; risultato: 1' –

+0

Questo codice usa 'eval' nella sua forma pericolosa. E 'terribilmente facile rompere il codice e anche fare cose pericolose (considera cosa succede con '$ validationRe =' /; print" Sei stato pwned \ n "; exit -1; # '' o '$ ans =' die" Questo codice è rotto! "; # ''). Poiché si interpola persino '$ ans', ci sono pochissimi casi in cui questo funzionerà davvero. Hai mai provato questo codice? – amon

+0

Sì amon ho testato questo codice. puoi vedere un esempio nel mio primo commento. Se provi a giocare senza una corretta convalida, qualsiasi cosa può accadere. L'utente stava cercando un modo per leggere il modificatore da variabile, ho solo suggerito in un modo. –

2

sbarazzarsi del vostro parametro $caseSensitive, come sarà inutile in molti casi. Al contrario, gli utenti di tale funzione possono codificare le informazioni necessarie direttamente nella regex $validationRe.

Quando si crea un oggetto regex come qr/foo/, il motivo è in quel punto compilato nelle istruzioni per il motore regex. Se si stringifica un oggetto regex, si otterrà una stringa che, una volta interpolata in un'espressione regolare, avrà esattamente lo stesso comportamento dell'oggetto regex originale. Soprattutto, ciò significa che tutti i flag forniti o omessi dal letterale dell'oggetto regex saranno preservati e non possono essere sovrascritti! Questo è di progettazione, in modo che un oggetto regex continui a comportarsi in modo identico indipendentemente dal contesto in cui viene utilizzato.

Questo è un po 'asciutto, quindi usiamo un esempio. Ecco una funzione match che tenta di applicare un paio di regex simili a un elenco di stringhe. Quale abbinerà?

use strict; 
use warnings; 
use feature 'say'; 

# This sub takes a string to match on, a regex, and a case insensitive marker. 
# The regex will be recompiled to anchor at the start and end of the string. 
sub match { 
    my ($str, $re, $i) = @_; 
    return $str =~ /\A$re\z/i if $i; 
    return $str =~ /\A$re\z/; 
} 

my @words = qw/foo FOO foO/; 
my $real_regex = qr/foo/; 
my $fake_regex = 'foo'; 

for my $re ($fake_regex, $real_regex) { 
    for my $i (0, 1) { 
     for my $word (@words) { 
      my $match = 0+ match($word, $re, $i); 
      my $output = qq("$word" =~ /$re/); 
      $output .= "i" if $i; 
      say "$output\t-->" . uc($match ? "match" : "fail"); 
     } 
    } 
} 

uscita:

"foo" =~ /foo/ -->MATCH 
"FOO" =~ /foo/ -->FAIL 
"foO" =~ /foo/ -->FAIL 
"foo" =~ /foo/i -->MATCH 
"FOO" =~ /foo/i -->MATCH 
"foO" =~ /foo/i -->MATCH 
"foo" =~ /(?^:foo)/  -->MATCH 
"FOO" =~ /(?^:foo)/  -->FAIL 
"foO" =~ /(?^:foo)/  -->FAIL 
"foo" =~ /(?^:foo)/i -->MATCH 
"FOO" =~ /(?^:foo)/i -->FAIL 
"foO" =~ /(?^:foo)/i -->FAIL 

In primo luogo, dobbiamo notare che la rappresentazione di stringa di oggetti regex ha questo strano (?^:...) modulo. In un gruppo non catturante (?: ...), i modificatori per il modello all'interno del gruppo possono essere aggiunti o rimossi tra il punto interrogativo ei due punti, mentre lo ^ indica il set predefinito di contrassegni.

Ora, quando osserviamo la frase regex falsa che in realtà è solo una stringa che viene interpolata, possiamo vedere che l'aggiunta del flag /i fa la differenza come previsto. Ma quando usiamo un oggetto regex reale, non cambia nulla: l'esterno /i non può sostituire i flag (?^: ...).

Probabilmente è meglio presumere che tutte le espressioni regolari siano già oggetti regex e non dovrebbero essere interferite con. Se carichi i modelli regex da un file, dovresti richiedere che le espressioni regolari utilizzino la sintassi (?: ...) per applicare la tecnica flages (ad esempio come equivalente a qr/foo/i). Per esempio. caricamento di un'espressione regolare per riga da un handle di file potrebbe essere simile a:

my @regexes; 
while (<$fh>) { 
    chomp; 
    push @regexes, qr/$_/; # will die here on regex syntax errors 
} 
Problemi correlati