Problem mit RegExp und Ersetzung

04/01/2012 - 09:50 von Winfried Vorbeck | Report spam
Hallo,

Dieter Nuhr hat Unrecht, wenn er sagt "was einmal im Keller war, kommt
nicht wieder". Meine Perl-Programmierung lagerte ueber 10 Jahre im
Keller und kommt jetzt wieder ;-) Nun habe ich ein Problem; ich hoffe,
hierzugroup gibt es keine GoogleGroups-Vorbehalte.

Folgendes: ich habe eine Datei, die in einer Zeile so etwas wie
Licensee="Diese Lizenz ist falsch"
stehen hat und durch so etwas wie
Licensee="Jetzt habe ich die richtige Lizenz Dies|ist|die Lizenz"
ersetzt werden soll.

Ich dachte, ich mache das so (etwas verkuerzt):

open(INIFILE, "< $fn") || die ("konnte die Datei \"$fn\" nicht öffnen.
Fehler: $!");
@eingabe = <INIFILE>;
close DATEI;

foreach $line (@eingabe) {
if ($line =~ m/^Licensee=\"(.*)\"$/i) {
$old = $1;
$line =~ s/$old/$licensee/o;
}
push(@ausgabe, $line);
}

open(INIFILE, "> $fn") || die ("konnte die Datei \"$fn\" nicht öffnen.
Fehler: $!");
print INIFILE @ausgabe;
close INIFILE;

Beim ersten Aufruf klappt das wunderbar. Rufe ich das Perl-Skript
erneut auf, wird bei jedem Aufruf alles ab dem ersten Pipe-Zeichen ein
weiteres Mal angehàngt, so dass die Zeile nach dem zweiten Aufruf so
aussieht:
Licensee="Jetzt habe ich die richtige Lizenz Dies|ist|die Lizenz|ist|
die Lizenz"

Bei anderen Werten ohne Pipe-Zeichen funktioniert das Skript und zwar
unabhàngig von der Umgebung (Windows XP oder Ubuntu).

Was mache ich flacsh?

Winfried
 

Lesen sie die antworten

#1 Peter J. Holzer
04/01/2012 - 12:26 | Warnen spam
On 2012-01-04 08:50, Winfried Vorbeck wrote:
Hallo,

Dieter Nuhr hat Unrecht, wenn er sagt "was einmal im Keller war, kommt
nicht wieder". Meine Perl-Programmierung lagerte ueber 10 Jahre im
Keller und kommt jetzt wieder ;-) Nun habe ich ein Problem; ich hoffe,
hierzugroup gibt es keine GoogleGroups-Vorbehalte.



Nur dann, wenn Google Umlaute vermurkst, Zeilenumbrüche an unpassender
Stelle einfügt oder sonstwie die Lesbarkeit vermindert. Wenn Du es
schaffst, mit GoogleGroups lesbare Postings zu produzieren, soll es uns
recht sein.

Folgendes: ich habe eine Datei, die in einer Zeile so etwas wie
Licensee="Diese Lizenz ist falsch"
stehen hat und durch so etwas wie
Licensee="Jetzt habe ich die richtige Lizenz Dies|ist|die Lizenz"
ersetzt werden soll.

Ich dachte, ich mache das so (etwas verkuerzt):

open(INIFILE, "< $fn") || die ("konnte die Datei \"$fn\" nicht öffnen.
Fehler: $!");
@eingabe = <INIFILE>;
close DATEI;

foreach $line (@eingabe) {
if ($line =~ m/^Licensee=\"(.*)\"$/i) {
$old = $1;
$line =~ s/$old/$licensee/o;
}
push(@ausgabe, $line);
}

open(INIFILE, "> $fn") || die ("konnte die Datei \"$fn\" nicht öffnen.
Fehler: $!");
print INIFILE @ausgabe;
close INIFILE;



Hat mit Deinem Problem nichts zu tun (dazu komme ich noch) aber:

1) Wenn Du ohnehin das ganze File einliest, könntest Du das gleich in
einem Scalar statt einem Array machen, entweder indem Du $/ = undef
setzt oder File::Slurp verwendest. Dann sparst Du Dir auch die
Schleife.

2) Bare File Handles und die 2-Argument-Form von open gelten inzwischen
als veraltet. Besser wàre

open(my $infile, '<', $fn) or die ...


Beim ersten Aufruf klappt das wunderbar. Rufe ich das Perl-Skript
erneut auf, wird bei jedem Aufruf alles ab dem ersten Pipe-Zeichen ein
weiteres Mal angehàngt, so dass die Zeile nach dem zweiten Aufruf so
aussieht:
Licensee="Jetzt habe ich die richtige Lizenz Dies|ist|die Lizenz|ist|
die Lizenz"

Bei anderen Werten ohne Pipe-Zeichen funktioniert das Skript und zwar
unabhàngig von der Umgebung (Windows XP oder Ubuntu).



Es würde auch nicht funktionieren, wenn Klammern, Punkte, Sterne,
Pluszeichen oder andere Metazeichen im String vorkàmen.

In
$line =~ s/$old/$licensee/o;


ist $old eine Regexp (hast Du ja im Subject selbst geschrieben). Du
ersetzt in

$line =~ s/Jetzt habe ich die richtige Lizenz Dies|ist|die Lizenz/xxx/;

nicht den String "Jetzt habe ich die richtige Lizenz Dies|ist|die Lizenz"
durch "xxx", sondern den ersten Teilstring, der die die Regexp matcht.
"|" in einer Regexp bedeutet "oder". Du matcht also
Jetzt habe ich die richtige Lizenz Dies
oder
ist
oder
die Lizenz


Was mache ich flacsh?



Siehe oben.

Es gibt mehrere Arten, das richtig zu machen:

1) Warum matcht Du zuerst, und führst dann die Ersetzung in einem
zweiten Schritt durch? s/// matcht ja sowieso, also reicht:

foreach $line (@eingabe) {
s/^(Licensee=").*(")$/$1$licensee$2/;
push(@ausgabe, $line);
}

2) Du kannst alle Metacharacters in $old quoten:

foreach $line (@eingabe) {
if ($line =~ m/^Licensee=\"(.*)\"$/i) {
$old = $1;
$line =~ s/\Q$old\E/$licensee/o;
}
push(@ausgabe, $line);
}

3) Nach dem Match weißt Du ja genau, wo der zu ersetzende String beginnt
und aufhört, Du musst ihn also nicht noch einmal matchen und kannst
ihn direkt ersetzen:

foreach $line (@eingabe) {
if ($line =~ m/^Licensee=\"(.*)\"$/i) {
substr($line, $-[1], $+[1] - $-[1]) = $licensee;
}
push(@ausgabe, $line);
}

Die erste Variante ist die, die ich bevorzugen würde. Die dritte ist
m.E. sauberer als die zweite, aber ich gebe zu, dass ich nicht auswendig
wusste, dass die Variablen für Beginn und Ende eines Matchs @- und @+
heißen und erst nachsehen musste - jemand der den Code liest, muss also
wahrscheinlich auch erst im Manual nachsehen, um es zu verstehen.

hp

_ | Peter J. Holzer | Deprecating human carelessness and
|_|_) | Sysadmin WSR | ignorance has no successful track record.
| | | |
__/ | http://www.hjp.at/ | -- Bill Code on

Ähnliche fragen