ich: Suchen und ersetzen in Datei: Speicherzugriffsfehler

Tach!

Ich habe ein Problem mit perl -p -e 's/); /);\n/g' dummy

Da dummy zwischen 6 und 7 Gigabyte groß ist führt das zu der Medlung "Speicherzugriffsfehler". dummy ist übrigens der echte Name. Den habe ich so gewählt, weil es den Inhalt in der Form nur temporär gibt.

In dummy gibt es gegenwärtig keine Zeilenumbrüche und es ist alles eine Zeile. Das möchte ich damit ändern.

Ich suche nach Alternativen, die sich an der Dateigröße nicht stören.

MfG
ich

  1. Hi ich!

    Ich suche nach Alternativen, die sich an der Dateigröße nicht stören.

    Ich kenne mich mit Perl ja nun überhaupt nicht aus. :-)
    Aber vielleicht wäre es eine Möglichkeit, immer nur eine bestimmte Anzahl an Zeichen (ein bis zwei Milliönchen) herauszupicken, bestimmte Zeichen zu ersetzen, in eine neue Datei schreiben, und bei der alten fortfahren, bis das Dateiende erreicht ist.

    MfG H☼psel

    --
    "It's amazing I won. I was running against peace, prosperity, and incumbency."
    George W. Bush speaking to Swedish Prime Minister unaware a live television camera was still rolling, June 14, 2001
    Selfcode: ie:% fl:( br:> va:) ls:& fo:) rl:? n4:& ss:| de:] js:| ch:? sh:( mo:) zu:)
    1. Tach.

      Ich kenne mich mit Perl ja nun überhaupt nicht aus. :-)

      Ich auch nicht wirklich. Ich brauche nur einen Befehl, den man da an der Komandozeile eingeben kann - so wie den, den ich probiert habe. Im Grunde genommen ist es egal, ob dazu Perl oder etwas anderes genutzt wird. Hauptsache ich bekomme da statt Leerzeichen einen Zeilenumbruch hinter jede ");" folge.

      Das Thema habe ich aber bei Perl einsortiert, weil ich es damit versucht habe.

      MfG
      ich

      1. Hell-O!

        Ich brauche nur einen Befehl, den man da an der Komandozeile eingeben kann - so wie den, den ich probiert habe.

        Mit einem Befehl ist es nicht getan, du brauchst ein Script, das deine Datei block- bzw. zeichenweise einliest und wegschreibt. Wird ein bestimmtes Muster gefunden, dann wird nicht das gelesene sondern ein Zeilenumbruch weggeschrieben. Mal so runtergetippt:

        my $source = $ARGV[0];  
        my $target = $ARGV[1];  
        my $char = '';  
        open IN, $source;  
        open OUT, ">$target";  
        flock OUT, LOCK_EX;  
        while(!eof(IN)) {  
          # naechstes Zeichen lesen  
          $char .= getc(IN);  
          print STDOUT "$char\n";  
          # naechster Durchlauf, wenn ) oder ; am Ende stehen  
          next if $char =~ /[);]$/;  
          # Suchen und Ersetzen  
          $char =~ s/\); /\);\n/;  
          # Wegschreiben  
          print OUT $char;  
          # $char leeren  
          $char = '';  
        }  
        close(IN);  
        close(OUT);
        

        So müsste es funktionieren.

        Siechfred

        --
        Ich bin strenggenommen auch nur interessierter Laie. (molily)
        Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
        1. Tach.

          So müsste es funktionieren.

          Ja, das läuft. Aber es ist viel zu langsam um praxistauglich zu sein 10 MB in 5 Minuten...

          Das würde einige Tage laufen.

          1. Tach.

            So müsste es funktionieren.

            Ja, das läuft. Aber es ist viel zu langsam um praxistauglich zu sein 10 MB in 5 Minuten...

            Habe auch mal die Bildschirmausgabe der gelesenen Zeichen rausgenommen - etwa 80 MB in 2 Minuten - schon besser...

            Hattest Du das da zum debuggen eingebaut? Ich hab's nicht gleich gesehen.

            1. Hell-O!

              Habe auch mal die Bildschirmausgabe der gelesenen Zeichen rausgenommen - etwa 80 MB in 2 Minuten - schon besser...
              Hattest Du das da zum debuggen eingebaut? Ich hab's nicht gleich gesehen.

              Ja, das hatte ich zum Debuggen eingebaut und bei C&P vergessen, rauszulöschen.

              Siechfred

              --
              Ich bin strenggenommen auch nur interessierter Laie. (molily)
              Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
          2. Also mein Angebot steht noch.
            Meine Lösung braucht auf einem alten PC für 2GB 10-15 min incl. Dateinbankeinträge und umfangreichen Suchmethoden.
            Da ich Deine Mail-Adresse nicht habe, kann ich auch nix verschicken.

            gruß
            David

            1. Hell-O!

              Meine Lösung braucht auf einem alten PC für 2GB 10-15 min incl. Dateinbankeinträge und umfangreichen Suchmethoden.

              Magst du sie uns zeigen? Man lernt schließlich nie aus.

              Siechfred

              --
              Ich bin strenggenommen auch nur interessierter Laie. (molily)
              Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
              1. Der Kram ist zu umfangreich.
                Ich kanns gerne an jeden verschicken, der mir seine Mail gibt.
                Gruß
                David

      2. Also, der Ansatz von Hopsel ist bei der Dateigröße wichtig, da sonst wahrscheinlich versucht wird die gesamte Datei in den Speicher einzulesen.

        Ich habe dazu in PHP mal eine streamingfähige Funktion geschrieben, die die Datei immer nur Blockweise einliest. Eine Änderung der Daten habe ich nicht implementiert, da ich meine Dateien nur auslesen wollte. Ich habe das mit Dateien getestet, die ca. 2 GB groß waren (also wesentlich größer als mein damaliger Speicher).

        Vermutlich ändert sich bei Dir die Dateigröße (da Du ja Leerzeichen einfügen willst). Deswegen würde ich die alte Datei nicht überschreiben, sondern (falls Du noch HDD-Platz hast) eine neue Schreiben, wo die entspr. Manipulationen durchgeführt wurden. Ansonsten müßte meine Funktion geändert werden, da sich die Zeiger sonst mit jedem eingefügten Zeichen zusätzlich verschieben müssen.

        Du kannst die kommentierten Funktionen mit Kommentaren bei mir per Mail anfordern, Dateien mit Testaufrufen habe ich ebenfalls.
        Die Dateien sind zu umfangreich, um sie hier zu posten.

        Ich würde übrigens empfehlen, die Datei zu splitten oder in einer anderen Form zu speichern, da 10GB allgemein schwer zu handeln sind.
        Ich habe meine Daten damals mit obigen Funktionen in einer Datenbank registriert und wusste dann zumindest wo ich nach bestimmten Dingen suchen muss. Der Einsatz der Funktionen ist aber Variabel und kann auch anders eingesetzt werden.

        Gruß
        David

        1. Hell-O!

          Also, der Ansatz von Hopsel ist bei der Dateigröße wichtig, da sonst wahrscheinlich versucht wird die gesamte Datei in den Speicher einzulesen.

          Ja, genau das ist das Problem.

          Ich habe dazu in PHP mal eine streamingfähige Funktion geschrieben, die die Datei immer nur Blockweise einliest.

          Was, wenn die Zeichenfolge "); " beim blockweisen Lesen zerschnitten wird?

          Eine Änderung der Daten habe ich nicht implementiert, da ich meine Dateien nur auslesen wollte.

          Aber genau das ist hier der Knackpunkt, weshalb blockweises Einlesen der Datei zu unerwünschten Effekten führen kann.

          Siechfred

          --
          Ich bin strenggenommen auch nur interessierter Laie. (molily)
          Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
          1. stimmt.
            Meine Funktion bedenkt das.

            Gruß
            David

            1. Meine Funktion bedenkt das.

              Und in welcher Form?

              Siechfred

              --
              Ich bin strenggenommen auch nur interessierter Laie. (molily)
              Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
              1. Hallo Sechfred,

                auszug aus meinem Skript:

                // Butterfly-Funktionalität:
                 // Bei Übergabe von 2 $buffern wird die Grenze des ersten soweit überschritten wie
                 // strlen($newcasearray)-1 damit auch der Grenzbereich untersucht wird

                Tut mir leid, aber die Veröffentlichung hier scheitert am Umfang.
                Ich hatte es getestet.

                Gruß
                David

                1. // Bei Übergabe von 2 $buffern wird die Grenze des ersten soweit überschritten wie
                  // strlen($newcasearray)-1 damit auch der Grenzbereich untersucht wird

                  Ah, verstehe. Letztlich macht mein Vorschlag allerdings auch nichts anderes, nur dass er zeichenweise statt blockweise einliest.

                  Siechfred

                  --
                  Ich bin strenggenommen auch nur interessierter Laie. (molily)
                  Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
                  1. Hallo Siechfred,

                    // Bei Übergabe von 2 $buffern wird die Grenze des ersten soweit überschritten wie
                    // strlen($newcasearray)-1 damit auch der Grenzbereich untersucht wird

                    Ah, verstehe. Letztlich macht mein Vorschlag allerdings auch nichts anderes, nur dass er zeichenweise statt blockweise einliest.

                    auch wenn normalerweise aus dem Cache gelesen werden sollte, kann das blockweise Einlesen die Geschwindigkeit der Gesamtoperation möglicherweise deutlich steigern. In der O-Notation sind beide Verfahren sicher gleich schnell - aber auch ein konstanter Faktor wäre hier wünschenswert. Viel umfangreicher sollte Dein Skript dennoch nicht werden.

                    Freundliche Grüße

                    Vinzenz

                  2. stimmt, zeichenweises einlesen dauert sehr lange.
                    strlen($newcasearray) muss immer so lang sein wie der jeweilige Suchbegriff. Da dann 1 subtrahiert wird, besteht keine Gefahr, daß der gleiche Begriff zweimal an der gleichen Stelle gefunden wird.
                    Ich habe eine Suchfunktion wo ich auch Arrays übergeben kann und wo die Länge des Überschneidenden Bereichs abhängig von der des Suchbegriffs bestimmt wird.

                    Gruß
                    David

  2. Hey,
    den Speicherzugriffsfehler erhältst du, weil du tatsächlich alles in ein $_ einzulesen versuchst. Siehe http://perldoc.perl.org/perlrun.html#*-p*--p, das -p wird zu einem while mit <> expandiert. Bekanntlich ist <> nur die Kurzform für $_ = readline(*STDIN);.

    Schau dir mal den Schalter -0 an. Gib ihm den oktalen Wert für ), nämlich 051. Jetzt wird ) als Zeilenende interpretiert. Wenn dann am Zeilenanfang ; und Leerzeichen steht, ersetze es mit ; und \n.

    perl -0051 -pe's/^; /;\n/' dummy

    Diese Lösung über chunkweises Einlesen wird nochmals schneller sein als die vorher gepostete über zeichenweises Einlesen.

    --
    水-金-地-火-木-土-天-海-冥
    1. Hell-O!

      Schau dir mal den Schalter -0 an. Gib ihm den oktalen Wert für ), nämlich 051. Jetzt wird ) als Zeilenende interpretiert. Wenn dann am Zeilenanfang ; und Leerzeichen steht, ersetze es mit ; und \n.

      *vor die Stirn klatsch* klar, der Input record separator. An das Naheliegendste denkt man oft nicht :-)

      Das bringt mich doch noch auf die Idee, meinen Vorschlag ein wenig umzubauen, denn nicht jeder kann/darf/will Perl auf der Kommandozeile ausführen:

      local $/ = '); ';  
      open IN, $source;  
      open OUT, ">$target";  
      while(<IN>) {  
        $_ =~ s/\); /\);\n/;  
        print OUT $_;  
      }  
      close(IN);  
      close(OUT);
      

      Das dürfte m.E. die mit Abstand schnellste Script-Variante werden.

      Siechfred

      --
      Ich bin strenggenommen auch nur interessierter Laie. (molily)
      Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
      1. local $/ = '); ';

        open IN, $source;
        open OUT, ">$target";
        while(<IN>) {
          $_ =~ s/); /);\n/;
          print OUT $_;
        }
        close(IN);
        close(OUT);

        
        >   
        > Das dürfte m.E. die mit Abstand schnellste Script-Variante werden.  
          
        Die RegEx brauchst du nicht, da das Zeilentrennzeichen ja mit eingelesen wird (du  kannst es aber mit chomp() entfernen).  
          
        ~~~perl
        local $/ = '); ';  
        open IN, $source;  
        open OUT, ">$target";  
        while(<IN>)  
        {  
           print OUT "$_\n";  
        }  
        close(IN);  
        close(OUT);
        

        Das dürfte nochmal eine ganze Menge bringen, da RegExen langsam sind

        Struppi.

        --
        Javascript ist toll (Perl auch!)
        1. Hell-O!

          Die RegEx brauchst du nicht, da das Zeilentrennzeichen ja mit eingelesen wird (du  kannst es aber mit chomp() entfernen).

          Hm, die bessere Wahl wäre m.E. chop, da aus dem Leerzeichen ja ein Newline werden soll, und das Leerzeichen wird immer am Ende der Zeile stehen. Deine Variante belässt das Leerzeichen dort, wo es ist, ich denke, dass das der OP so nicht möchte.

          Das dürfte nochmal eine ganze Menge bringen, da RegExen langsam sind

          Da hast du ohne Zweifel Recht.

          Siechfred

          --
          Ich bin strenggenommen auch nur interessierter Laie. (molily)
          Siechfreds Tagebuch || Falle Aufteilungsbescheid || RT 221 Erfurt-Altstadt i.V.
          1. Die RegEx brauchst du nicht, da das Zeilentrennzeichen ja mit eingelesen wird (du  kannst es aber mit chomp() entfernen).

            Hm, die bessere Wahl wäre m.E. chop, da aus dem Leerzeichen ja ein Newline werden soll, und das Leerzeichen wird immer am Ende der Zeile stehen. Deine Variante belässt das Leerzeichen dort, wo es ist, ich denke, dass das der OP so nicht möchte.

            Ach so, ich hatte den etwas ausgeuferten Thread nur überflogen, ja dann dürfte chop richtiger und sinnvoller sein.

            Struppi.

            --
            Javascript ist toll (Perl auch!)