Kai Diefenbach: Zeilenumbrüche

Hallo Forum, Cheatah, Klaus

ich moechte noch mal auf das Thema "Zeilenumbrueche" zurueckkommen, das wir vor etwas laengerer Zeit hier diskutiert haben.
http://www.teamone.de/selfhtml/sfarchiv/2000_3/t17767.htm#a90288

Frage war damals wie man mehrfache Zeilenumbrueche (>2) filtert.
Cheatah kam auf folgende Loesung:

my $umbruch = '(\015\012\015\012)';
s/($umbruch{2})$umbruch+/$1/g;

welche auch funktioniert : )

Allerdings frage ich mich mittlerweile warum.

Unter Win/Dos muesste die RegExp IMHO schon auf zwei Umbrueche, sprich \015\012\015\012 matchen.

Der Grund ist m.E. folgender:

Die RegExp auf \015\012\015\012 angewandt, fuehrt _nur_ mit der ersten Alternative zu keinem Treffer.
Damit ein Treffer erzielt werden kann, wird auf die zweite bzw. dritte Alternative "ausgewichen".
Es steht also somit nur \015\012\015 in $1.

Allerdings verhaelt sie nicht auf diese Art, sondern so wie gewollt.

Wenn ich allerdings '\015' mit 'a' und '\012' mit 'b' ersetze, verhaelt sie sich so, wie ich es erwarten wuerde.

Mit folgenden Skripten kann man das nachvollziehen:

<Skript1>

#!perl -w

use strict;
undef $/;
my $text = <DATA>;
my $umbruch = '(\015\012\015\012)';
print "matched\n" if $text=~ s/($umbruch{2})$umbruch+/$1/g == 1;
__DATA__
eins

zwei
</Skript1>

Gibt nix aus.

<Skript2>
#'\015' mit 'a' und '\012' mit 'b' ersetzt
#!perl -w
use strict;
my $text = 'einsababzwei';
my $umbruch = '(abab)';
print "matched\n" if $text=~ s/($umbruch{2})$umbruch+/$1/g == 1;
</Skript2>

Gibt 'matched' aus.

Kann mir jemand erklaeren, warum das so ist ?

Gruss
   Kai

  1. Hallo Forum, Cheatah, Klaus,

    richtiger Link ist:
    <../../sfarchiv/2000_3/t18527.htm#a93811>

    Sorry
       Kai

  2. Hallo Kai,

    Frage war damals wie man mehrfache Zeilenumbrueche (>2) filtert.
    Cheatah kam auf folgende Loesung:

    my $umbruch = '(\015\012\015\012)';
    s/($umbruch{2})$umbruch+/$1/g;

    welche auch funktioniert : )

    Allerdings frage ich mich mittlerweile warum.

    »»

    Mit folgenden Skripten kann man das nachvollziehen:

    <Skript1>
    [...]

    hast Du evtl. mal geschaut, was den so in $text steht?

    $text =~ s/(.)/sprintf('%#o',ord($1))/seg;
    print $text;

    zeigt mir jedenfalls, daß Perl beim einlesen von Textdateien automatisch aus einm \015\012 ein \012 macht. In deinem Text steht also nur \012\012 - und da funktioniert die RegExp auch. Wenn Du jedoch per "binmode DATA;" diese Konvertierung abschaltest ...

    Wo Du die Dateien schon so einliest, hast Du es eigentlich mal mit "$/ = '';" probiert? Hier werden automatisch beim einlesen mehr als zwei Zeilenumbrüche auf zwei Reduziert. Ansonsten wird der Text sozusagen Absatzweise eingelesen.

    Möchtest Du hingegen einen Text aus einer Variablen bearbeiten, so solltest Du folgendes verwenden:
    print "matched\n" if $text=~ s/($umbruch)\1{2,}/$1$1/g == 1;

    Durch die Referenz auf die erste Klammer wird die Art und Weise festgehalten, wie eine einzele Zeilenschaltung aufgebaut ist. Dies muß dann mindestens noch zwei weitere male auftreten. Ist dies der Fall so soll nur zweimal die Zeilenschaltung übrig bleiben.

    Viel Spaß,
       Jörk

    1. Hallo Joerk

      hast Du evtl. mal geschaut, was den so in $text steht?

      $text =~ s/(.)/sprintf('%#o',ord($1))/seg;
      print $text;

      zeigt mir jedenfalls, daß Perl beim einlesen von Textdateien automatisch aus einm \015\012 ein \012 macht. In deinem Text steht also nur \012\012 - und da funktioniert die RegExp auch. Wenn Du jedoch per "binmode DATA;" diese Konvertierung abschaltest ...

      Hey, das scheint mir die Loesung zu sein.
      Danke, war ich doch schon fast verzweifelt ; )

      [viele andere gute Tipps]

      Danke, fuer die wirklich guten Anregungen !

      Viel Spaß,

      Jepp !

      Jörk

      Gruss
         Kai

    2. Hallo Jörk, ;-)

      Noch ein kleiner Nachtrag:

      Möchtest Du hingegen einen Text aus einer Variablen bearbeiten, so solltest Du folgendes verwenden:
      print "matched\n" if $text=~ s/($umbruch)\1{2,}/$1$1/g == 1;

      Durch die Referenz auf die erste Klammer wird die Art und Weise festgehalten, wie eine einzele Zeilenschaltung aufgebaut ist. Dies muß dann mindestens noch zwei weitere male auftreten. Ist dies der Fall so soll nur zweimal die Zeilenschaltung übrig bleiben.

      Mag ja sein, daß man durchaus mal Zeilenschaltungen aus unterschiedlichen Systemen in seinem Text hat. Demzufolge wäre mein Ansatz doch etwas sehr einschränkend :( Bevor man jetzt aber denn Weg geht, alle \r heraus zu schmeißen bzw. in ein \n zu wandeln:
      print "matched\n" if $text=~ s/($umbruch{2})$umburch+/$1/g == 1;

      Uups, kennen wir doch schon! Richtig die Änderung liegt ja auch in $umbruch:
      my $umbruch = '(?>\015\012\015\012)';

      Hierdurch entfällt das Backtracking-Problem: Eine einmal festgelegte Zeilenschaltung (also eine der drei Möglichkeiten aus $umbruch) wird nicht wieder geändert! Wurde also z.B. \015\012 zweimal in \015\012\015\012 gefunden, so wird hinterher nicht versucht, für den zweiten Treffer nur \015 zu verwenden, so daß \012 als vermeindliche dritte Zeilenschaltung verwendet werden könnte.

      Gruß,
         Jörk