2017-07-22 3 views
0

Ich versuche, ein Perl-Skript zu erstellen, das auf STDIN präsentierte Daten filtert, alle Vorkommen von eine Zeichenfolge in eine andere ändert und alle eingegebenen Zeilen unverändert und unverändert in STDOUT ausgibt. FROMSTRING und TOSTRING können PERL-kompatible reguläre Ausdrücke sein. Ich kann keine übereinstimmende Ausgabe erhalten.Perl-Filter mit Substitution

Hier ist ein Beispiel für das, was ich versuche zu erreichen.

echo "Today is Saturday" | f.pl 'a' '@' 

Ausgabe [email protected] is [email protected]@y.

echo io | filter.pl '([aeiou])([aeiou])' '$2$1' 

Ausgabe oi.

#!/usr/bin/perl 
use strict; 
use warnings; 
if (@ARGV != 2){ 
     print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n" 
} 
exit 1; 
my $FROM = $ARGV[0]; 
my $TO = $ARGV[1]; 
my $inLine = ""; 
while (<STDIN>){ 
$inLine = $_; 
$inLine =~ s/$FROM/$TO/; 
print $inLine 
} 
exit 0; 
+0

Hinweis - kann 'while (my $ inline = ) {...}'. Also, 'my ($ from, $ to) = @ARGV;' – zdim

+0

Oder 's/$ from/$ to/r während ;' mit '/ r' modifier [from 5.14] (http: // perldoc. perl.org/perl5140delta.html) – zdim

Antwort

0

drei Fehler gefunden:

; after error message 
exit 1; 
$inLine =~ s/$FROM/$TO/g; 

wie:

#!/usr/bin/perl 
use strict; 
use warnings; 
if (@ARGV != 2){ 
     print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n"; 
     exit 1; 
} 
my $FROM = $ARGV[0]; 
my $TO = $ARGV[1]; 
my $inLine = ""; 
while (<STDIN>){ 
$inLine = $_; 
$inLine =~ s/$FROM/$TO/g; 
print $inLine 
} 
exit 0; 
+0

Danke, das waren nur einfache Syntaxfehler, die ich übersehen habe. Wenn ich jedoch etwas Komplexeres wie filter.pl versuche ([aeiou]) ([aeiou]) '' $ 2 $ 1'' Es wird nicht korrekt vervollständigt. – user8351474

+0

Sie brauchen kein ';' nach der letzten Anweisung in einem Block. – melpomene

+0

@ user8351474 Das '$ 2 $ 1'' ist eine literale Zeichenfolge (nur diese vier Zeichen) und so wird es in der Regex behandelt. Sie erhalten also das Ergebnis von $ 2 $ 1. Sie müssen also diese Eingabezeichenfolge mit ihrer Bedeutung in der Regex angeben (in diesem Fall die Variablen $ 2 und $ 1), was nicht einfach ist. Siehe [dieser Beitrag] (https://Stackoverflow.com/a/45233407/4653379) für die gleiche Art von Problem. – zdim

6

Erstens, das Ersatzteil eines s/.../.../ Betrieb ist nicht ein regulärer Ausdruck; Es funktioniert wie eine Zeichenfolge in doppelten Anführungszeichen.

Es gibt ein paar Probleme mit Ihrem Code.

  • Ihre exit 1; Aussage erscheint in der Mitte des Haupt-Code, nicht in dem Fehlerblock. Sie wollen wahrscheinlich:

    if (@ARGV != 2) { 
        print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n"; 
        exit 1; 
    } 
    
  • Sie verpassen eine g Flag, wenn Sie mehrere Substitutionen passieren in der gleichen Linie wollen:

    $inLine =~ s/$FROM/$TO/g; 
    
  • Es gibt keine Notwendigkeit zu predeclare $inLine; Es wird nur in einem Block verwendet.

  • Es ist auch nicht notwendig, eine Zeile in $_ zu lesen, nur um es in $inLine zu kopieren.
  • Es ist üblich, $names_like_this für Variablen und Funktionen zu verwenden, nicht $namesLikeThis.
  • Sie können $0 verwenden, anstatt den Programmnamen in der Fehlermeldung fest zu codieren.
  • exit 0; ist am Ende redundant.

Hier finden Sie näher an, wie ich es schreiben würde:

#!/usr/bin/perl 
use strict; 
use warnings; 

if (@ARGV != 2) { 
    die "Usage: $0 FROMSTRING TOSTRING\n"; 
} 

my ($from, $to) = @ARGV; 

while (my $line = readline STDIN) { 
    $line =~ s/$from/$to/g; 
    print $line; 
} 

Das heißt, keines dieser Adressen Ihr zweites Beispiel mit '$2$1' als Ersatz. Der obige Code wird nicht tun, was Sie wollen, weil $to eine einfache Zeichenfolge ist. Perl wird nicht nach Objekten wie $1 suchen und sie ersetzen. Wenn Sie "foo $bar baz" in Ihren Code schreiben, bedeutet das dasselbe wie 'foo ' . $bar . ' baz', aber dies gilt nur für CodeZeug, das buchstäblich in Ihrem Quellcode erscheint. Der Inhalt von $bar wird zur Laufzeit nicht erneut gescannt, um z. \n oder $quux. Dies gilt auch für $1 und Freunde, die nur normale Variablen sind.

So wie erhalten Sie '$2$1' zu arbeiten?

Eine Möglichkeit ist Kampf mit eval, aber ich mag es nicht, weil, na ja, es eval ist: Wenn Sie nicht sehr vorsichtig sind, wäre es jemand erlauben, beliebigen Code auszuführen, indem Sie den richtigen Ersatz „String übergeben ".

Doing it ohne eval ist möglich und sogar einfach mit z.B. Data::Munge::replace:

#!/usr/bin/perl 
use strict; 
use warnings; 
use Data::Munge qw(replace); 

if (@ARGV != 2) { 
    die "Usage: $0 FROMSTRING TOSTRING\n"; 
} 

my ($from, $to) = @ARGV; 

while (my $line = readline STDIN) { 
    print replace($line, $from, $to, 'g'); 
} 

replace Werke wie die JavaScript-String#replace, dass es spezielle $ Sequenzen erweitert.

es von Hand zu tun, ist auch möglich, aber etwas ärgerlich, weil man im Grunde $to als Vorlage zu behandeln hat, und erweitern Sie alle $ Sequenzen von Hand (zB durch eine andere regex Substitution verwenden):

# untested 
$line =~ s{$from}{ 
    my @start = @-; 
    my @stop = @+; 
    (my $r = $to) =~ s{\$([0-9]+|\$)}{ 
     $1 eq '$' 
      ? '$' 
      : substr($from, $start[$1], $stop[$1] - $start[$1]) 
    }eg; 
    $r 
}eg; 

(Dies tut Umsetzung nicht verspannten Gruppen wie ${1}, ${2} usw. Diese sind für den Leser als Übung.)

Dieser Code ausreichend ärgerlich ist, zu schreiben (und sehen), dass ich viel lieber ein Modul wie Data::Munge für th mit ist eine Sache.

+0

Gut, dieses Modul zu erwähnen. Vielleicht ein hagerer Kommentar zur Verwendung von 'readline' anstelle der Operatorform' <> '? Ich denke, Sie bevorzugen es für die Lesbarkeit und kanonische Bedeutung in der EDV (und ich beschwere mich nicht!), Aber es kann einen Anfänger verwirren (wie in warum-this-and-not-that)? – zdim

+0

Ich benutze nie '<' '>' wegen seiner Ad-hoc-syntaktischen Überladung: '<$foo>' bedeutet 'readline ($ foo)' aber '<${foo}>' und '<$ foo>' bedeutet 'glob ($ foo)'. Es ist viel klarer (und unzweideutiger), in allen Fällen nur 'readline' und' glob' zu schreiben. (Bonus: Nicht-Perl-Programmierer verstehen, was vor sich geht.) – melpomene

+0

Ich dachte es und stimme dem Argument vollkommen zu. Ich habe mich auf die Antwort bezogen, die auf einen Anfänger abzielte. Aber das ist viel mehr als ein kurzer Kommentar (der auch nicht notwendig ist). – zdim