Re: Suchen und Ersetzen in großer XML-Datei mit kruden Zeichen - wie gehts am besten?
Hallo,
Agon S. Buchholz schrieb:
Hi,
ich möchte in einer relativ großen XML-Datei (>1,5 GB) einige Tags,
Kommentare und CDATA-Abschnitte global ersetzen. Als GUI-verwöhnter
Bequemling dachte ich erst, dass das ziemlich trivial sein müsste, bin
dann aber ein paarmal gegen die Wand gerannt. Bestimmt hat hier jemand
mehr Erfahrung und kann mir ein wenig auf die Sprünge helfen.
Zum einen handelt es sich wohl um eine Datei in UTF-8, die auch Unicode
bleiben muß (welche Tools sind wirklich Unicode-clean?), zum anderen
steht mir auf dem betreffenden Rechner nur 512 MB RAM zur Verfügung (ein
beliebiger Editor scheidet wohl aus, weil sich bspw. nano oder vi an der
Datei totladen).
#!/usr/bin/perl -w
use strict;
my $quelle = 'hier_den_Namen_der_Quelldatei_angeben';
my $ziel = 'hier_den_Namen_der_Zieldatei_angeben';
open DATEI1, "<", $quelle or die "konnte $quelle nicht oeffnen, $!\n";
open DATEI2, ">", $ziel or die "konnte $ziel nicht oeffnen, $!\n";
while ( $zeile = <DATEI1> ) {
# hier alle Ersetzungen in der Zeile vornehmen
# ein Vorschlag dazu weiter unten [*]
# und diese Zeile gleich wieder abspeichern
print DATEI2 $zeile;
}
close DATEI1;
close DATEI2;
# hinterher die Quelldatei loeschen und die Zieldatei umbenennen
__END__
bei der Variante ist m.E. nur eine Zeile der Datei im Ram und wird
sofort wieder geschrieben. Allerdings brauchst du temporär den doppelten
Festplattenplatz.
Ich dachte dann, dass ich mir die Datei einfach auf meinen lokalen
Rechner herunterladen und hier verarbeiten könnte, so richtig gut lässt
sich das XML aber nicht komprimieren (Upload der editierten Datei dauert
mit ADSL-Upstream ewig). Last but not least habe ich beim Googeln ob der
Fülle möglicher Tools dann ein wenig den Überblick verloren, was
wirklich geeignet wäre und womit ich mir womöglich eher zusätzliche
Probleme einhandele, zumal die zu ersetzenden Strings nicht nur
einfachen Text, sondern auch "krude" Zeichen wie <, /, | und [
enthalten; ein einfaches sed 's/string1/string2/g' FILE funktionert also
nicht ohne weiteres [4]; ähnliches gilt wohl für xargs, perl -ipe und
replace.
Verkürzt gesagt sieht der S&R-Ausdruck in etwa so aus: Aus
"</string1>"
soll werden
"<NEWLINE>[[{{string2|{{value}}}}]]<NEWLINE></string1>",
dann würde ich oben [*] nicht mit $zeile =~ s/<irgendwas>/<nochetwas>/g
arbeiten, sondern würde die Strings einfach zusammensetzen, z.B. so:
$zeile = "\n" . '[[{{' . $string2 . '|{{' . $value;
$zeile .= '}}}}]]' . "\n". '</'.$string1 . '>' . "\n";
(ich habe nur wegen der besseren Lesbarkeit zwei Zeilen daraus gemacht.)
was mir nicht klar ist, wo der string 2herkommt, nimmst du die aus einer
Zuordnungstabelle (Hash) oder wird der irgendwie gebaut aus string1? -
verstehe ich jetzt nicht. :(
wobei <NEWLINE> möglichst ein Zeilenumbruch bzw. eine neue Zeile werden
soll (wie "\n" bzw. "\r" in Perl); letzteres dient aber, wenn ich die
XML-Datei richtig lese, nur der Formatierung eines CDATA-Abschnitts.
Würde etwa folgendes funktionieren [2] [3], oder geht es unter Debian
auch irgendwie einfacher (sprich: übersichtlicher)? Irgendwie macht mich
diese Maskierung völlig wuschig ;-/
perl -i.bak -p -e
's/\<\/string1\>/\n\[\[\{\{string2\|\{\{value\}\}\}\}\]\]\n\<\/string1\>/ig'
FILE.xml
ich habe noch nie mit Perl-Einzeilern gearbeitet - sorry.
Beim weiteren Googeln stieß ich auf [5], der die zu ersetzenden Strings
nicht mit "\", sondern mit "|" trennt ("perl -p -i[.ext] -e 's|x|y|[ig]'
filename") - was ist denn nun eigentlich richtig?
bei perl ist es so, dass du *jedes* Zeichen als Begrenzer verwenden kannst.
es geht z.B.
$zeile =~ s/x/y/g; oder
$zeile =~ s~x~y~g; oder
$zeile =~ s,x,y,g; oder
$zeile =~ s|x|y|g;
in der ersten Zeile ist der Begrenzer "/" und wenn er in einem Ausdruck
auftritt, (also statt x bzw. statt y), dann muss er eben mit "\"
maskiert werden. wenn das eine URL ist, dann würde ich doch glatt die
Zeile 3 nehmen, weil in einer URL normalerweise kein Komma vorkommt
(Ausnahme LDAP-URLs)
Vor längerer Zeit wurde hier mal ein nettes Skript gepostet [1]; so
etwas ähnliches bräuchte ich, nur ohne die dortige Einschränkung: "Bei
Sonderzeichen wie !"'`´§$ usw. kann es zu Fehlern führen, dieses
Programm ist zum suchen und ersetzen normaler Texte gedacht [...]"...
weitere Anmerkung. es gibt ab perl 5.6 die Schreibweise \p{...} - da
werden diese Ausdrücke in der geschweiften Klammer als
unicode-formatiert betrachtet und verwendet. habe ich aber noch nicht
damit gearbeitet.
[1]
http://www.pa-s.de/php/script-P91SE-V.0.9.1---suchen-und-ersetzen-f%FCr-Linux-54.php
[2] Nach Lektüre von http://www.perl.com/pub/a/2004/10/14/file_editing.html
[3] Zusammengebaut nach "Maskierung von Zeichen in regulären Ausdrücken"
in http://de.selfhtml.org/perl/sprache/regexpr.htm
[4] Mittlere Ratlosigkeit nach Lesen von
http://www.faqs.org/faqs/editor-faq/sed/
[5]
http://www.sns.ias.edu/~jns/wp/2006/02/15/how-to-perform-in-place-editing-of-a-file-using-perl/
bin leider auch nur Perl-Autodidakt mit ganz wenig Erfahrung. vielleicht
kann ich dir mehr helfen, wenn du dein Problem noch genauer beschreibst.
einen Perl-Einzeiler kann ich dir aber definitiv nicht liefern, sondern
nur ein Script wie oben. Und wenn du nicht mit 2 Dateien arbeiten kannst
(Plattenplatz), dann muss ich leider auch passen.
Mit freundlichen Grüßen
Hans-Dietrich
Reply to: