[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: