Lev Benenson: Loeschen leere Zeilen (newline)

Hi, alle,

es gibt ein Problem.
Es gibt eine Datei, bestehend aus vielen Zeilen. Einige Zeilen bestehen nur aus Newline (leere Zeilen). Diese Zeilen möchte ich mit einen Perlscript loeschen. Bis jetzt waren alle meine Versuche erfolgslos.

Fuer jede Hilfe waere ich dankbar.

Lev Benenson

  1. Hi Lev!

    es gibt ein Problem.

    *Schock*

    Es gibt eine Datei, bestehend aus vielen Zeilen. Einige Zeilen bestehen nur aus Newline (leere Zeilen). Diese Zeilen möchte ich mit einen Perlscript loeschen. Bis jetzt waren alle meine Versuche erfolgslos.

    Wann kommt das Problem?? ;-)

    Gut, ich würde es so machen, dass ich die Datei zeilenweise abarbeite und in eine temp-Datei schreibe. Wenn eine leere Zeile vorkommt (regEx dafür wäre glaube ich /^$/), schreibt er die nicht in die Temp, ansonsten schreibt er alles.

    Am Schluß dann die alte löschen und die Temp-Datei umbenennen.

    -- Diese Lösung ist allerdings für Dateien mit ständigem Zugriff etwas schwierig, weil je nach Größe natürlich große Ladezeiten auftreten.

    Bis denne,
    Dennis

    1. hallo,

      oder Du machst es so:

      open(IN, $dateiname);
      @Zeilen = <IN>;
      close(IN);
      open(OUT,">$dateiname");
      foreach $zeile (@zeilen)
         {
         chomp $zeile; #entfernt das NEWLINE
         $zeile =~ s/\s*$//; #optional entfern leerzeichen am ende vom string
         next unless $zeile; #weiter wenn Zeile leer ist
         print OUT "$zeile\n";
        }
      close(OUT);

      Grüße
      Klaus

      1. Hallöle!

        ... so geht es auch (ist ja nur 'n anderer Programmierstil ;-)), man muss allerdings immer noch sagen, dass es bei größeren Dateien zu Langwierigkeiten kommt!

        'Ne andere Lösung wüßt ich aber so auch nicht...

        Bis denne,
        Dennis

      2. Hi Klaus!

        open(IN, $dateiname);

        Wie immer: open(IN, "<$dateiname") die("Can't open $dateiname for reading because: $!");

        @Zeilen = <IN>;
        close(IN);
        open(OUT,">$dateiname");
        foreach $zeile (@zeilen)
           {
           chomp $zeile; #entfernt das NEWLINE
           $zeile =~ s/\s*$//; #optional entfern leerzeichen am ende vom string
           next unless $zeile; #weiter wenn Zeile leer ist

        Vorsicht: Das schlaegt auch an, wenn in der Zeile eine einzige 0 steht (wird dann als die Zahl 0 interpretiert, und das ist logisch false). Daher:
           next unless length($zeile); #weiter wenn Zeile leer ist

        print OUT "$zeile\n";
          }
        close(OUT);

        So long

      3. Hallo Lev/Klaus,

        ich würde es so machen:

        hallo,

        oder Du machst es so:

        open(IN, $dateiname);

        Rückgabewert testen

        open IN, $dateiname or die "$!";
        open OUT, ">$dateiname" or die "$!";

        @Zeilen = <IN>;
        close(IN);
        open(OUT,">$dateiname");

        In diesem Fall besteht IMHO kein Grund, die gesamte Datei in ein Array zu lesen. Dies kann sehr Speicherintensiv werden.

        foreach $zeile (@zeilen)
           {
           chomp $zeile; #entfernt das NEWLINE
           $zeile =~ s/\s*$//; #optional entfern leerzeichen am ende vom

        »»    string

        next unless $zeile; #weiter wenn Zeile leer ist
           print OUT "$zeile\n";
          }
        close(OUT);

        while (<IN>) {
           print OUT unless /^$/;
        }

        close IN;
        close OUT;

        Grüße
        Klaus

        Gruß
          Kai

        1. hallo,

          open IN, $dateiname or die "$!";
          open OUT, ">$dateiname" or die "$!";

          ja ja, ich war schlampig.

          In diesem Fall besteht IMHO kein Grund, die gesamte Datei in ein Array zu lesen. Dies kann sehr Speicherintensiv werden.

          es ist immer die Frage, was gerade besser ist, nur mit einer Datei arbeiten oder mit zweien. wenn die Datei gecht groß ist, dann ist es zweifellos besser, mit einer zwischendatei zu arbeiten.
          ein zweiter Grund könnte sein, daß die Datei auch von anderen Prozessen, z.B. dem Webserver immer gebraucht wird. dann ist es besser die Datei kurz vorher erst zu entfernen und die zwsichendatei umzubenennen.

          Mit  einer Zwischendatei würde ich das Array auch weglassen:

          while (<IN>) {

          »»    print OUT unless /^$/;

          }

          auch hier wieder Vorischt:
          eine leerzeile ist nicht wirklich leer, und <IN> bringt dann ein '\n' daher,also
          while (<IN>)
             {
             chomp;
             print OUT unless /^$/;
             }

          aber, um das ganze abzukürzen, sollte der geneigte Programmierer sich die Datei ansehen, und dann entscheiden, welchen Weg er beschreiten will.
          weil wie sagt's Larry so treffen: TMTOWTDI

          grüße
          Klaus

          1. Hallo Klaus,

            [...]

            while (<IN>) {
            »»    print OUT unless /^$/;
            }
            auch hier wieder Vorischt:
            eine leerzeile ist nicht wirklich leer, und <IN> bringt dann ein '\n' daher,also
            while (<IN>)
               {
               chomp;

            Nur der Vollstaendigkeit halber:

            ein chomp ist hier nicht noetig, weil der Zeilenende-Anker $, (u.a.) in Perl, auf das Ende des gesamten Strings, sowie vor einem Newline am Ende des Strings passt - und zweiteres ist ja hier der Fall.

            print OUT unless /^$/;
               }

            [...]

            grüße
            Klaus

            Gruss
               Kai

            1. ein chomp ist hier nicht noetig, weil der Zeilenende-Anker $, (u.a.) in Perl, auf das Ende des gesamten Strings, sowie vor einem Newline am Ende des Strings passt - und zweiteres ist ja hier der Fall.

              Hallo Kai,

              mein Irrtum, 'tschuldige.
              sollte wieder mal das Kleingeschriebene lesen.

              schönen Abend noch,
              klaus

            2. Hi!

              Nur der Vollstaendigkeit halber:

              ein chomp ist hier nicht noetig, weil der Zeilenende-Anker $, (u.a.) in Perl, auf das Ende des gesamten Strings, sowie vor einem Newline am Ende des Strings passt - und zweiteres ist ja hier der Fall.

              Nein, das gilt nur, wenn der Modifier m angegeben wird, also
                print OUT unless /^$/m;
              Ohne diesen matcht ein ^ wirklich nur den Anfang des Strings (nicht den einer Zeile innerhalb des Strings) und $ nur das Ende des Strings. Siehe perldoc perlre -> Abschnitt Description.

              So long

              1. Hallo Calobyte,

                Hi!

                print OUT unless /^$/m;
                Ohne diesen matcht ein ^ wirklich nur den Anfang des Strings (nicht den einer Zeile innerhalb des Strings) und $ nur das Ende des Strings. Siehe perldoc perlre -> Abschnitt Description.

                Sorry, *das* ist falsch, denn perldoc perlre sagt:

                By default, the ^'' character is guaranteed to match only the beginning of the string, the $'' character only the end *(or before the newline at the end)*
                [...]
                You may, however, wish to treat a string as a multi-line buffer, such that the ^'' will match after any newline within the string, and $'' will match before any newline. At the cost of a little more overhead, you can do this by using the /m modifier on the pattern match operator
                [...]

                Das meint: bei /m passt $ auf jedes \n, auch mitten im String.

                Vergl. auch: Regular Expressions, O'Reilly, J. Friedl S. 237 (ff)

                So long

                Gruss
                  Kai

                1. Re Hi!

                  By default, the ^'' character is guaranteed to match only the beginning of the string, the $'' character only the end *(or before the newline at the end)*

                  Mmh, tatsaechlich, soweit hab ich gar nicht gelesen (ganz oben bei der ersten Erklaerung von m stands naemlich nicht). Offen gesagt, finde ich diese Sonderbehandlung ein bisschen doof (weil unlogisch). Gibt es eine Moeglichkeit, das abzustellen?

                  So long

                  1. Hi Ray,

                    Re Hi!

                    By default, the ^'' character is guaranteed to match only the beginning of the string, the $'' character only the end *(or before the newline at the end)*

                    Mmh, tatsaechlich, soweit hab ich gar nicht gelesen (ganz oben bei der ersten Erklaerung von m stands naemlich nicht). Offen gesagt, finde ich diese Sonderbehandlung ein bisschen doof (weil unlogisch). Gibt es eine Moeglichkeit, das abzustellen?

                    Nein, "$" passt immer (mind.) auf Ende und "\n" vor Ende. Ist IMHO kein Bug sondern ein Feature, da sich genau o.g. Aufgaben
                    effizient loesen lassen.

                    Wenn explizit das Ende des Strings gemeint ist, kann man

                    (?!\n)$    #(?!\n) -> "naechstes Zeichen, darf kein \n sein"

                    benutzen.

                    So long

                    Gruss
                      Kai

                    1. Re Hi!

                      Nein, "$" passt immer (mind.) auf Ende und "\n" vor Ende. Ist IMHO kein Bug sondern ein Feature, da sich genau o.g. Aufgaben
                      effizient loesen lassen.

                      Nun ja, das faellt wohl unter Geschmackssache. Nicht jede Vereinfachung ist wirklich hilfreich im Programmierhandwerk, weil sie meist an anderer Stelle wieder Probleme mit sich bringt. Deshalb mag ich Perl im Grunde genommen auch nicht so sehr, weil es viele Dinge uebertrieben stark vereinfacht und einem damit die Kontrolle entzieht.

                      Wenn explizit das Ende des Strings gemeint ist, kann man
                      (?!\n)$    #(?!\n) -> "naechstes Zeichen, darf kein \n sein"

                      Ich hab in perlre jetzt noch \z gefunden, das man anstelle von $ einsetzen kann. Nicht schoen, weil gewaltige Umstellung (allerorten wird immer nur $ benutzt), aber wenn's hilft.

                      So long