Probleme mit filesize > 1 GB???
Matthias
- perl
Hallo Forum,
gibt es Probleme, wenn perl (sun solaris) Eingabedateien mit einer Größe über 1 GB verarbeiten muß? Wir lesen per sqlplus Daten aus Oracle und lassen in diesen per Perl-Script Zeichen löschen:
sqlplus -s -l ${user}/${psw}@${db} @$IIB_BASE_PATH/sql/exf1.sql | $IIB_BASE_PATH/bin/delpl.sh
Löschscript delpl.sh:
#!/opt/perl/bin/perl -w
use strict;
for (;<STDIN>;){
$_ =~ s/\x20//ig;
$_ =~ s/\x0A//ig;
print $_;
}
Und erhalten dabei seit der sqlplus-Output statt bisher 700MB 1,2 GB umfaßt, den Fehler :
Substitution loop at /as934a/soft/fkr/si/fkr/osp40/iib/bin/confpl.sh line 6, <STDIN> line 1.
Liegt das an der Dateigröße?
Frägt
Matthias Greß
for (;<STDIN>;){
Nimm mal stattdessen while(<...>)
kann sein, dass for hier die komplette Datei einliest.
Struppi.
Hallo Struppi,
das war's leider nicht.
Matthias
Substitution loop at /as934a/soft/fkr/si/fkr/osp40/iib/bin/confpl.sh line 6, <STDIN> line 1.
Also die Fehlermeldung bedeutet:
http://search.cpan.org/~nwclark/perl-5.8.8/pod/perldiag.pod
Substitution loop
(P) The substitution was looping infinitely. (Obviously, a substitution shouldn't iterate more times than there are characters of input, which is what happened.) See the discussion of substitution in Quote and Quote-like Operators.
Struppi.
gudn tach!
Substitution loop at /as934a/soft/fkr/si/fkr/osp40/iib/bin/confpl.sh line 6, <STDIN> line 1.
Liegt das an der Dateigröße?
sollte nicht.
koennte ein perl-bug sein. ist die perl-version schon betagt?
prost
seth
Hallo Seth,
hallo Struppi,
hallo Forum,
die Perl-Version:
This is perl, v5.8.1 built for sun4-solaris
Copyright 1987-2003, Larry Wall
Zum Thema "The substitution was looping infinitely. " ist anzumerken, daß alles funkt, wenn der Oracle-Ouput < 1 GB ist.
Gruß
Matthias
Hallo Forum,
gibt es Probleme, wenn perl (sun solaris) Eingabedateien mit einer Größe über 1 GB verarbeiten muß? Wir lesen per sqlplus Daten aus Oracle und lassen in diesen per Perl-Script Zeichen löschen:
sqlplus -s -l ${user}/${psw}@${db} @$IIB_BASE_PATH/sql/exf1.sql | $IIB_BASE_PATH/bin/delpl.sh
Löschscript delpl.sh:
#!/opt/perl/bin/perl -w
use strict;
for (;<STDIN>;){
$_ =~ s/\x20//ig;
$_ =~ s/\x0A//ig;print $_;
}Und erhalten dabei seit der sqlplus-Output statt bisher 700MB 1,2 GB umfaßt, den Fehler :
Substitution loop at /as934a/soft/fkr/si/fkr/osp40/iib/bin/confpl.sh line 6, <STDIN> line 1.
Liegt das an der Dateigröße?
Frägt
Matthias Greß
Zum Thema "The substitution was looping infinitely. " ist anzumerken, daß alles funkt, wenn der Oracle-Ouput < 1 GB ist.
Evtl. hilft es schon, wenn du die Daten nicht komplett an STDIN gibst, sondern erst als Datei auf der Festplatte speicherst und dann Perl Zeilenweise einlesen läßt.
Struppi.
Hell-O!
Noch 'ne Idee:
$_ =~ s/\x20//ig;
$_ =~ s/\x0A//ig;
$_ =~ s/\s//ig;
Oder wenn dir die Zeichenklasse \s zu umfangreich ist:
$_ =~ s/(\n| )//ig;
Vielleicht liegt's an der Schreibweise. Ansonsten weisen die Ergebnisse der Google-Suche auf einen immer wieder auftauchenden Perl-Bug hin, vielleicht ist es das.
Siechfred
Hi,
$_ =~ s/(\n| )//ig;
Besser:
$_ =~ s/[\n ]//ig;
Deine Version ist zwar korrekt, aber weniger effizient:
1. Speichern von Backreferences-Klammern vermeiden, wenn diese nicht benötigt werden. Wenn ein Ausdruck keine Backreferences-Klammern enthält, wird der gesamte Overhead für die Speicherung der Backreferences nicht benötigt.
2. Alternative | vermeiden, wenn eine Zeichenklasse ausreicht - Alternative führt zu teurem backtracking, Zeichenklassen nicht.
Und da der Ausdruck hier ja einige Male angewendet werden soll (geht ja eigentlich um ein Mengenproblem), macht sich das durchaus bemerkbar.
cu,
Andreas
Hi,
Besser:
$_ =~ s/[\n ]//ig;
Und noch besser:
$_ =~ s/[\n ]//g;
Das i-flag wird nicht benötigt, ist ja kein Buchstabe im Ausdruck.
cu,
Andreas
$_ =~ s/(\n| )//ig;
Besser:
$_ =~ s/[\n ]//ig;
Sicher, aber ...
- Speichern von Backreferences-Klammern vermeiden, wenn diese nicht benötigt werden [...]
Wird sowas beim Compilieren der regex nicht automatisch wegoptimiert, wenn's nicht gebraucht wird?
Zumindest bei sed macht's keinen (großen) Unterschied:
$ cat test.sh
#! /bin/sh
ls -l "$1" | awk '{ print $5 }'
set -m
( time sed -e 's/[ab]//g' < "$1" | wc -c ) > test.1.result 2>&1 &
( time sed -e 's/(a|b)//g' < "$1" | wc -c ) > test.2.result 2>&1 &
wait
wait
echo "Fertig"
cat test.1.result test.2.result
$ ./test.sh *5x18*
368261120
Fertig
365070706
real 5m31.565s
user 1m42.798s
sys 0m2.552s
365070706
real 5m47.543s
user 1m51.596s
sys 0m2.522s
- Alternative | vermeiden, wenn eine Zeichenklasse ausreicht - Alternative führt zu teurem backtracking, Zeichenklassen nicht.
Dito ...
Gruß, Bodo
Mea culpa.,
da beschäftigen sich die Spezialisten tagelang mit einer Perl-Sequenz und ich merke erst jetzt, dass 25 gleichzeitige Projekte einfach zu viel sind. Denn ich habe das falsche Skript gepostet. Das gepostete erste läuft jetzt dank Andreas et. all Tipps wie am Schnürchen, Bauchschmerzen bereitet mir aber dieses :
#!/opt/perl/bin/perl -w
use strict;
while (<STDIN>){
print STDERR tell(STDIN);
$_ =~ s/\x5B\x50\x5B\xff\x02/\x0A/g ;
$_ =~ s/\x5B\x50\x5B\xff\x03/\x0D/g ;
$_ =~ s/\x5B\x50\x5B\xff\x04/\x20/g ;
$_ =~ s/\x5B\x50\x5B\xff\x05/\x19/g ;
$_ =~ s/\x5B\x50\x5B\xff\x01/\x00/g ;
$_ =~ s/\x5B\x50\x5B//g;
print $_;
}
In der Hoffnung Gnade zu finden, habe ich mir ein paar Gedanken und Fingerübungen gemacht. Das erwähnte erste Skript erzeugt eine 1,2 GB große Ausgabedatei quasi ohne Punkt und Komma – sprich Zeilenumbrüche. Nun kommt dieses Monster auf das zweite (obige) Perlscript zu. Wenn ich vor der ersten Ersetzungszeile‚ tell(STDIN) absetze, erhalte ich den Wert -1. Ich habe auch probiert, den Output des ersten Scripts in eine Datei zu schaufeln und diese dann per
cat datei | perl.script
zu verarbeiten, was zum gleichen Fehler führt.
Das Script in der Form
1 #!/opt/perl/bin/perl -w
2 use strict;
3 my $Datei = "exf.del";
4 open(DATEI, "<$Datei") || die "$Datei: $!";
5 #while (<STDIN>){
6 while (<DATEI>){
7 print STDERR "So Weit\n";
8 print STDERR tell(DATEI)."\n";
9 $_ =~ s/\x5B\x50\x5B\xff\x02/\x0A/g ;
10 $_ =~ s/\x5B\x50\x5B\xff\x03/\x0D/g ;
11 $_ =~ s/\x5B\x50\x5B\xff\x04/\x20/g ;
12 $_ =~ s/\x5B\x50\x5B\xff\x05/\x19/g ;
….
ergibt:
So Weit
1205690315
Substitution loop at confpl.sh line 9, <DATEI> line 1.
und scheint das Dateiende nicht zu erkennen.
Sorry
Matthias
Wen’s interessiert was das Ganze soll: es geht darum, von SQL*Plus erzeugten EBCDIC-Code durch’s System zu schleusen und dabei den unterschiedlichen Codierung der White Spaces / Ziffern gerecht zu werden und binär codierte Längenangaben zu erhalten. Hierbei wurde immer das Muster '\x5B\x50\x5B\xff' vor zu substitutionierende "Zeichen" gesetzt.
Hell-O!
Das erwähnte erste Skript erzeugt eine 1,2 GB große Ausgabedatei quasi ohne Punkt und Komma – sprich Zeilenumbrüche. Nun kommt dieses Monster auf das zweite (obige) Perlscript zu.
Also wird es beim Einlesen als *eine* Zeile behandelt, sofern du $/ nicht modifiziert hast. Starker Tobak für das arme System :-)
print STDERR tell(DATEI)."\n";
1205690315
Du befindest dich am Ende der Datei, bereits beim ersten Lesevorgang, was meine obige Vermutung stützt. Vielleicht solltest du die Datei besser zeichen- bzw. blockweise einlesen, siehe hierzu getc und Term::ReadKey.
Dein Substitution Loop könnte hierin begründet sein:
$_ =~ s/\x5B\x50\x5B\xff\x02/\x0A/g;
$_ =~ s/\x5B\x50\x5B\xff\x03/\x0D/g;
$_ =~ s/\x5B\x50\x5B\xff\x04/\x20/g;
$_ =~ s/\x5B\x50\x5B\xff\x05/\x19/g;
da du in der ersten Zeile bereits alles ersetzt hast, was in den nächsten drei Zeilen gesucht ist. Auch ein chomp $_;
vor der Ersetzung könnte eventuell helfen.
Sind alles nur Vermutungen, vielleicht hilft's dir ja weiter.
Siechfred
$_ =~ s/\x5B\x50\x5B\xff\x02/\x0A/g;
$_ =~ s/\x5B\x50\x5B\xff\x03/\x0D/g;
$_ =~ s/\x5B\x50\x5B\xff\x04/\x20/g;
$_ =~ s/\x5B\x50\x5B\xff\x05/\x19/g;
>
> da du in der ersten Zeile bereits alles ersetzt hast, was in den nächsten drei Zeilen gesucht ist. Auch ein `chomp $_;`{:.language-Perl} vor der Ersetzung könnte eventuell helfen.
Naja, ohne Zeilemumbrüche....
Aber schau dir den Code nochmal an, er ersetzt schon jedesmal was anderes
Struppi.
--
[Javascript ist toll](http://javascript.jstruebig.de/) (Perl auch!)
Hell-O!
Naja, ohne Zeilemumbrüche....
[...]
Aber schau dir den Code nochmal an, er ersetzt schon jedesmal was anderes
So gesehen ... ist es einfach zu warm heute :-)
Siechfred
Hallo Struppi, Sichfried,
jo, bei Dateien < 1 GB tut das. Beim stückchenweisen Einlesen sthe ich ja vor dem Problem, daß ggf. das Muster \x5B\x50\x5B\xff\ durchschnitten wird.
Gruß
Matthias
jo, bei Dateien < 1 GB tut das. Beim stückchenweisen Einlesen sthe ich ja vor dem Problem, daß ggf. das Muster \x5B\x50\x5B\xff\ durchschnitten wird.
Das kann man lösen.
Du kannst aber u.U. auch einen eigenen Datei Zeilentrenner definieren und $/ zuweisen und dann die Datei zeilenweise einlesen.
Struppi.
Hallo Struppi,
scheint ein vielversprechender Ansatz zu sein. Hatte zwischenzeitlich auch mit awk/gsub experimentiert, der sagt aber zumindest, daß ihm der Eingabstrom zu lang ist.
Matthias
jo, bei Dateien < 1 GB tut das. Beim stückchenweisen Einlesen sthe ich ja vor dem Problem, daß ggf. das Muster \x5B\x50\x5B\xff\ durchschnitten wird.
Das kann man lösen.
Du kannst aber u.U. auch einen eigenen Datei Zeilentrenner definieren und $/ zuweisen und dann die Datei zeilenweise einlesen.
Struppi.
Hallo Struppi,
im Prinzip sieht es nun so aus:
$/= "\x5B\x50\x5B\xff";
$="";
print "Start: $CTIME_String\n";
open(EINDATEI, "<$Datei") || die "$Datei: $!";
open(AUSDATEI, ">$ausgDatei") || die "$ausgDatei: $!";
while (<EINDATEI>)
{
chomp $_;
$text = $_;
my $erstesZeichen = substr($text,0,1);
if ($erstesZeichen eq "\x02") {
print AUSDATEI "\x0A".substr($text,1 ) ;
} elsif ($erstesZeichen eq "\x03"){
print AUSDATEI "\x0D".substr($text,1 ) ;
} elsif ($erstesZeichen eq "\x04"){
print AUSDATEI "\x20".substr($text,1 ) ;
} elsif ($erstesZeichen eq "\x05"){
print AUSDATEI "\x19".substr($text,1 ) ;
} elsif ($erstesZeichen eq "\x01"){
print AUSDATEI "\x00".substr($text,1 ) ;
} else {
print AUSDATEI $text ;
}
}
und hat kein Problem mit Dateien > 1 GB. Sehr guter Tipp mit dem Zeilentrenner. Jetzt kommen noch die Tests, ob das Ergebnis stimmt.
Merci
Matthias
Hell-O!
Beim stückchenweisen Einlesen sthe ich ja vor dem Problem, daß ggf. das Muster \x5B\x50\x5B\xff\ durchschnitten wird.
Dann teste $_ auf \x5B, falls gefunden, dann lies die nächsten 3 Zeichen auch noch ein und ersetze sie entsprechend deiner Suchmuster.
Siechfred
7 print STDERR "So Weit\n";
8 print STDERR tell(DATEI)."\n";
....
ergibt:
So Weit
1205690315
Substitution loop at confpl.sh line 9, <DATEI> line 1.und scheint das Dateiende nicht zu erkennen.
Doch tell gibt dir doch den richtigen Wert aus.
du hast in diesem Beispiel nicht erwähnt, ob die Datei wenn sie kleiner ist, durchläuft?
Wenn ja, würde ich Siechfreds Rat testen und die Datei nur Stückchenweise einlesen.
Struppi.
Hallo.
Doch tell gibt dir doch den richtigen Wert aus.
Aber in Fränkli.
MfG, at