Matthias: Probleme mit filesize > 1 GB???

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ß

  1. for (;<STDIN>;){

    Nimm mal stattdessen while(<...>)

    kann sein, dass for hier die komplette Datei einliest.
    Struppi.

    --
    Javascript ist toll (Perl auch!)
    1. Hallo Struppi,

      das war's leider nicht.

      Matthias

      1. das war's leider nicht.

        die gleiche Fehlermeldung?

        Struppi.

        --
        Javascript ist toll (Perl auch!)
  2. 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.

    --
    Javascript ist toll (Perl auch!)
  3. 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

  4. 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ß

    1. 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.

      --
      Javascript ist toll (Perl auch!)
  5. 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

    --
    Hier könnte Ihre Werbung stehen.
    Viel Lärm um nichts || Steuerliche Einordnung des Rangrücktritts
    1. 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

      --
      Warum nennt sich Andreas hier MudGuard?
      Schreinerei Waechter
      O o ostern ...
      Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
      1. 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

        --
        Warum nennt sich Andreas hier MudGuard?
        Schreinerei Waechter
        O o ostern ...
        Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
      2. $_ =~ s/(\n| )//ig;

        Besser:
        $_ =~ s/[\n ]//ig;

        Sicher, aber ...

        1. 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

        1. Alternative | vermeiden, wenn eine Zeichenklasse ausreicht - Alternative führt zu teurem backtracking, Zeichenklassen nicht.

        Dito ...

        Gruß, Bodo

        1. 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.

          1. 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

            --
            Hier könnte Ihre Werbung stehen.
            Viel Lärm um nichts || Steuerliche Einordnung des Rangrücktritts
            1. $_ =~ 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!)
              
              1. 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

                --
                Hier könnte Ihre Werbung stehen.
                Viel Lärm um nichts || Steuerliche Einordnung des Rangrücktritts
              2. 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

                1. 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.

                  --
                  Javascript ist toll (Perl auch!)
                  1. 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.

                    1. 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

                2. 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

                  --
                  Hier könnte Ihre Werbung stehen.
                  Viel Lärm um nichts || Steuerliche Einordnung des Rangrücktritts
          2. 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.

            --
            Javascript ist toll (Perl auch!)
            1. Hallo.

              Doch tell gibt dir doch den richtigen Wert aus.

              Aber in Fränkli.
              MfG, at