2012-01-19 14 views
8

consideri un file di testo con il contenuto:Perl può sostituire più parole chiave con la propria parola sostitutiva in un colpo solo?

apple apple pear plum apple cherry pear apple 
cherry plum plum pear apple cherry pear pear apple plum 

e considerare la perl one-liner:

perl -pe "s/apple/green/g and s/pear/yellow/g and s/plum/blue/g and s/cherry/red/g" < fruits.txt 

Questo sostituisce ogni frutto con il suo colore.
Ora, questo potrebbe essere fatto in un singolo s /// g in qualche modo, invece dei quattro precedenti?

Sono anche preoccupato per l'ordine delle parole di frutta.
Se il mio campione non include "mela", nessuna delle altre sostituzioni verrà completata. Come dovrei risolvere quello?

Nota: desidero mantenere la soluzione come one-liner.
Quindi definire hash, leggere file e altre soluzioni che richiedono molte righe di codice perl non mi porta avanti.

È più una curiosità piuttosto che una questione di vita o di morte su cui un progetto dipenderebbe. Mi disturba solo da qualche giorno e ho pensato che un utente perl più esperto là fuori potesse aiutarti con la soluzione in un batter d'occhio, o mi avrebbe tolto dalla mia tristezza dicendomi chiaramente che questo non può essere fatto in perl nel modo che voglio.

+0

penso l'originale è più leggibile, ma si noti che il comportamento è diverso rispetto alle soluzioni proposte, se uno dei motivi o stringhe sono sottostringhe di altri modelli o stringhe (sostituzioni sequenziali vs un singolo passaggio) –

risposta

4
perl -E 'my %h = qw(apple green foo bar); say "apple foo" =~ s/(apple|foo)/$h{$1}/rge;' 
+0

Questo è il tipo di risposta che cercavo. La chiave è il modulo $ h {$ 1}, grazie per averlo indicato, ruz! Devo ammettere che mkb e ikegami hanno fornito una soluzione più completa e userò effettivamente qualcosa che hanno suggerito, ma contrassegnerò questa risposta come la vincente in quanto include la chiave che mi mancava. – Keve

5

perl -pe '%a=qw(apple green pear yellow plum blue cherry red);$b=join("|",keys %a);s/($b)/$a{$1}/g' < fruits.txt

+0

So che hai detto niente hash, ma è ancora una riga. – mkb

+0

Presumibilmente se le stringhe fossero note in anticipo, è possibile utilizzare un valore letterale hashref come parte dell'espressione sostitutiva? – Neil

+0

Chi ha downvoted questo? Questo è davvero elegante e funziona ancora come un unico rivestimento. –

11

Sostituire

perl -pe's/apple/green/g and s/pear/yellow/g and ...' fruits.txt 

con

perl -pe's/apple/green/g; s/pear/yellow/g; ...' fruits.txt 

Più veloce e non ha un problema con un => b b => c:

perl -pe' 
    BEGIN { 
     %subs=qw(apple green pear yellow plum blue cherry red); 
     $re=join "|", map quotemeta, keys %subs; 
     $re = qr/$re/; 
    } 
    s/($re)/$subs{$1}/g; 
' fruits.txt 

(È possibile rimuovere il « map quotemeta, » se input contiene niente di speciale.)

(È possibile rimuovere le interruzioni di riga che ho aggiunto per migliorare la leggibilità.)

+0

Ottima soluzione. Mi ci è voluto un po 'di tempo per comprendere appieno cosa fa esattamente la prima riga $ re = ..., ma ne è valsa la pena. Mi hai dato idee al di là della mia richiesta originale, sottolineando possibili problemi che avrei affrontato in seguito. Questa è la soluzione più completa che ho ricevuto alla mia domanda. Spero non ti dispiaccia che accetto la risposta di ruz! I punti extra sono più utili per lui che per te. – Keve

+0

@ Keve, grazie, nessun problema :) – ikegami

1

A seconda del problema, credo che avrei solo essere un po 'sciatto e osservare ogni sequenza di spazi non bianchi. Se è qualcosa di interessante, lo sostituisco. In caso contrario, rimetto lo stesso testo.

$ perl5.14.2 -nE 'print s/(\S+)/$h{$1}?$h{$1}:$1/rge}BEGIN{%h=qw(apple green pear yellow plum blue cherry red)' 

Se il problema è più complicato di così, il mio one-liner sarà simile:

$ perl fruits2color 

Molte delle altre risposte po 'su una regex unendo le stringhe. In un programma non one-liner, probabilmente lo farei con qualcosa come Regex::Assemble o Regexp::Trie. Quei moduli possono costruire alternanze efficienti.

+0

Sono impressionato dal tuo trucco di corde di ricambio, Brian! Questo mi piace molto. Ma le tue parentesi graffe non sono posizionate correttamente. Credo che la chiusura tra "/ rge" e "BEGIN" appartenga alla fine, seguendo "red") ma prima della singola citazione. Probabilmente hai spostato il blocco BEGIN dalla parte anteriore a quella posteriore. ;-) – Keve

+1

No, quelle parentesi sono nel posto giusto. Ce ne sono due nascosti -n che non vedi. Hai provato l'one-liner? –

+0

Hai ragione. In realtà, doppiamente così. Perché le parentesi nascoste sono lì a causa di -n, e No non ho provato prima di aprire la bocca. Mi dispiace per quello! – Keve

0

Chi ha detto che gli hash non riescono a ricordare il loro ordine :)?

How can I make my hash remember the order I put elements into it?

Usare il Tie::IxHash da CPAN.

use Tie::IxHash; 
tie my %myhash, 'Tie::IxHash'; 

for (my $i=0; $i<20; $i++) { 
    $myhash{$i} = 2*$i; 
} 

my @keys = keys %myhash; 
# @keys = (0,1,2,3,...) 

$ perl -MTie::IxHash -pe ' 
     BEGIN { tie %h, "Tie::IxHash"; 
       %h = qw< apple green pear yellow >; 
       } 
     s<($_)>/$h{$1}/g for keys %h; 
     ' file 
Problemi correlati