Tom: Zeilenendezeichen feststellen

Hello,

ein "Problemchen", das leider gar nicht so trivial ist, habe ich noch:

Wie müsste ein Algorithmus aussehen, der Zeilenendezeichen von Textdateien feststellen kann.

PHP hat zwar den Schalter "auto_detect_line_endings", der für viele Datei-Lesefunktionen eingesetzt werden kann, aber mich interesiert außerdem die Feststellung des Zeilenendezeichens.

Besonders schwierig wird es wohl, wenn innerhalb einer Datei blöderweise auch noch unterschiedliche EOL verwendet werden.

Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

Liebe Grüße aus dem schönen Oberharz

Tom vom Berg

--
 ☻_
/▌
/ \ Nur selber lernen macht schlau
http://bikers-lodge.com
  1. * 0D0A, das ist das klassische Zeilenendezeichen in Windows.
    * nur 0A ohne 0D davor, Linux
    * Vielleicht gibts auch mal nur 0D, ohne nachfolgendes 0A?

    Jetzt würd ich nach den Varianten suchen die vorkommen könnten. Wahrscheinlich nur die ersten beiden, dann suchst du nach 0A. Hier ist auf jeden Fall ein Zeilenende, entweder mit 0D davor oder nicht. Alle 0A suchen und schauen was davor steht, dann hast du rausgefunden was als Zeilenende verwendet wird.
    Alle vorkommen von 0A mit/ohne 0D davor ersetzen durch nur 0A/0D0A, dann ist alles einheitlich.

    Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

    Um automatisch zu erkennen welches Zeilenendezeichen nicht als Zeilenende zählen soll, gibts da irgendwie ein include(*MAGIC*) oder so?

    1. Hello,

      * 0D0A, das ist das klassische Zeilenendezeichen in Windows.
      * nur 0A ohne 0D davor, Linux
      * Vielleicht gibts auch mal nur 0D, ohne nachfolgendes 0A?

      Nur 0x0D ist mir heute leider passiert und mein System ist auf die Schnauze gefallen. Ich habe drei Stunden gesucht, bis ich den Fehler gefunden hatte. Eine Kollegin, die Vorarbeiten erledigt und eigentlich nur auf Windows arbeitet, hatte mir ein File untergeschoben, dass etliche (eigentlich nur 0x0D, die übrigen werde ich wohl dann selber reingebaut haben) dieser "Macenden" (leider keine Mettenden) enthalten hat.

      Da die Datei nun schon im Dirketaufruf (http) die ganze Zeit im System war und vom Browser einwandfrei angezeigt wurde, habe ich den relevanten Teil dann arglos ins Templateverezeichnis übernommen und diverse Platzhalter eingepflegt.

      Dann zeigte die Seite nichts mehr an. Normalerweise hätte eine Fehlernummer kommen müssen:

        
      function tpl_read($path)  
      {  
          if (!$fp = @fopen($path, 'rb')) return 5;   ## Kann File nicht lesen  
          if (!flock($fp, LOCK_SH)) return 8;         ## File lässt sich nicht sperren  
        
          $tpl = '';  
          while (!feof($fp))  
          {  
              $line = fgets ($fp, MAX_LINE_LEN);  
              if (mb_strlen($line) > (MAX_LINE_LEN - 4)) return 55;    ## Formatfehler in der Datei  
        
              $linecut = ltrim($line);  
              if (substr($linecut,0,1) != '#')  
              {  
                  $tpl .= $line;  
              }  
          }  
        
          fclose($fp);  
          return $tpl;  
      }  
      
      

      Die Datei hatte nur ca. 2600 Bytes, also weniger als 8004 Zeichen (MAX_LINE_LEN).
      MMn hätte die an einem Stück (einer Zeile) in $tpl landen müssen.
      Die Antwort war allerdings immer leer.

      Da die Funktion schon im Repository stand und bei allen anderen Templates einwandfrei funktionierte, bin ich zuerst nicht auf die Lösung gekommen, bis ich zum Schluss dann doch endlich den Hex-Editor benutzt habe *grmpf*

      Unser Kunde möchte später "einfache Seiten" selber hochladen können, also eigentlich nur Teile davon. Es ist nicht auszuschließen, dass er dann selber auch solchen Unsinn produziert. Das muss ich also abfangen.

      Für den HTML-Teil mag das stumpfe Replacen

        
      tausche "\r\n" -> geschütztes Zeichen  
      tausche "\r" -> "\r\n"  
      tausche "\n" -> "\r\n"  
      tausche geschütztes Zeichen -> "\r\n"  
        
      
      

      ausreichend sein.

      Ich benötige aber auch noch eine Variante, bei der dann PHP-Files und andere Textdateien hochgeladen werden können. Dass ein Admin auch Fehler macht, habe ich mir ja heute ganz plastisch selber vorgeführt.

      Ich möchte daher alle nicht innerhalb von Strings befindlichen Zeichen finden und ggf. tauschen.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bikers-lodge.com
      1. tausche "\r\n" -> geschütztes Zeichen
        tausche "\r" -> "\r\n"
        tausche "\n" -> "\r\n"
        tausche geschütztes Zeichen -> "\r\n"

          
          
        Umständlich. Das geht einfacher:  
          
        ~~~pseudocode
          
        define (LF, "\n\r") # or whatever  
        tausche "\r\n" -> "\n"  
        tausche "\r" -> "\n"  
        tausche "\n" -> LF  
        
        

        Jörg Reinholz

    2. Hallo,

      * 0D0A, das ist das klassische Zeilenendezeichen in Windows.

      und systemunabhängig in einigen Internet-Protokollen so vorgeschrieben (HTTP, SMTP).

      * nur 0A ohne 0D davor, Linux

      Ja, bis hin zu dessen Urahn Unix.

      * Vielleicht gibts auch mal nur 0D, ohne nachfolgendes 0A?

      Frühere Versionen von Mac OS, AFAIK. Und einige Heimcomputer-Systeme aus den 80er Jahren.

      Jetzt würd ich nach den Varianten suchen die vorkommen könnten. Wahrscheinlich nur die ersten beiden, dann suchst du nach 0A. Hier ist auf jeden Fall ein Zeilenende, entweder mit 0D davor oder nicht. Alle 0A suchen und schauen was davor steht, dann hast du rausgefunden was als Zeilenende verwendet wird.

      Ganz so einfach ist es nicht, aber mit absoluter Sicherheit kriegt man's wohl nicht raus. Man muss also mit einer gewissen Heuristik vorgehen:

      * Zähle alle Linefeeds (0x0A), denen kein CR (0x0D) vorausgeht
       * Zähle alle CR (0x0D), denen kein Linefeed (0x0A) folgt
       * Zähle alle Paare aus 0x0D und 0x0A

      Ist nun einer der drei ermittelten Werte _erheblich_ größer als die beiden anderen, hast du den verwendeten Code für den Zeilenumbruch gefunden; sind die anderen beiden Zähler 0, kann das Ergebnis als sicher gelten, sonst immerhin als "wahrscheinlich mit vereinzelten Contra-Indizien".

      Rein hypothetisch könnte auch noch LF/CR (also erst 0x0A, dann 0x0D) auftreten. Das habe ich aber in freier Wildbahn noch nicht angetroffen. Auch ein CR mit mehreren folgenden Linefeeds wäre denkbar (\r\n\n\n).

      Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

      Dann muss man eine verlässliche Methode haben, solche "Fremdabschnitte" zu erkennen, etwa eindeutige Begrenzungszeichen für Stringkonstanten (Anführungszeichen).

      So long,
       Martin

      --
      Es existiert kein Weg, "für" etwas zu optimieren, sondern nur gegen alles andere.
        (Cheatah)
      Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
      1. Hello,

        Rein hypothetisch könnte auch noch LF/CR (also erst 0x0A, dann 0x0D) auftreten. Das habe ich aber in freier Wildbahn noch nicht angetroffen. Auch ein CR mit mehreren folgenden Linefeeds wäre denkbar (\r\n\n\n).

        Naja, "kaputte" Dateien will ich gar nicht erst bearbeiten müssen

        Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

        Dann muss man eine verlässliche Methode haben, solche "Fremdabschnitte" zu erkennen, etwa eindeutige Begrenzungszeichen für Stringkonstanten (Anführungszeichen).

        Ich muss nochmal darüber nachsinnen, wann das überhaupt vorkommt. Es iat aber durchaus denkbar, dass ein File auf einer Plattform einen Text für die Ausgabe auf einer anderen Plattform enthält. Und denn will man dann ja nicht kaputt machen.

        Und ich weiß bis jetzt noch nicht, wieso die Datei nur 0x0D enthalten hat. Die wurde allerdings mit einem Dreamweaver erstellt. Vielleicht war der falsch eingestellt. Kann der sowas, also auf Windows Macenden produzieren?

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bikers-lodge.com
      2. Hallo

        * Vielleicht gibts auch mal nur 0D, ohne nachfolgendes 0A?

        Frühere Versionen von Mac OS, AFAIK. Und einige Heimcomputer-Systeme aus den 80er Jahren.

        Bis Mac OS 9.x war das der Umbruchcode von Mac.

        Jetzt würd ich nach den Varianten suchen die vorkommen könnten.

        Ganz so einfach ist es nicht, aber mit absoluter Sicherheit kriegt man's wohl nicht raus. Man muss also mit einer gewissen Heuristik vorgehen:

        * Zähle alle Linefeeds (0x0A), denen kein CR (0x0D) vorausgeht
        * Zähle alle CR (0x0D), denen kein Linefeed (0x0A) folgt
        * Zähle alle Paare aus 0x0D und 0x0A

        Abgesehen von Zeilenumbrüchen, die in einer CSV-Datei innerhalb eines Datenfeldes vorkommen, habe ich bei Bedarf folgendes Schema verwendet. Suche zuerst nach der Kombination "CRLF" ("\r\n"), dann nach einzeln stehenden "LF" ("\n") und zuletzt (weil wohl am seltensten auftretend) nach "CR" ("\r").

        Wenn man die oben angenommenen Umbrüche in Datenfeldern von der Behandlung ausschließen will, muss man eh ganz anders rangehen. Dann stehen die Datenfeldbegrenzer im Vordergrund der Prüfung.

        Tschö, Auge

        --
        Verschiedene Glocken läuteten in der Stadt, und jede von ihnen vertrat eine ganz persönliche Meinung darüber, wann es Mitternacht war.
        Terry Pratchett, "Wachen! Wachen!"
        ie:{ fl:| br:> va:) ls:[ fo:) rl:( ss:| de:> js:| zu:}
        Veranstaltungsdatenbank Vdb 0.3
  2. Moin!

    Besonders schwierig wird es wohl, wenn innerhalb einer Datei blöderweise auch noch unterschiedliche EOL verwendet werden.

    PHPStorm (empfehlenswerte IDE für PHP) macht es so: Das erste Zeilenendezeichen der Datei bestimmt, welche Endezeichen die Datei beim Speichern bekommt. Alle Zeilenendezeichen der Datei werden normalisiert und intern gar nicht erst unterschiedlich behandelt.

    Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

    Hä?

    - Sven Rautenberg

    1. Hello Sven,

      Besonders schwierig wird es wohl, wenn innerhalb einer Datei blöderweise auch noch unterschiedliche EOL verwendet werden.

      PHPStorm (empfehlenswerte IDE für PHP) macht es so: Das erste Zeilenendezeichen der Datei bestimmt, welche Endezeichen die Datei beim Speichern bekommt. Alle Zeilenendezeichen der Datei werden normalisiert und intern gar nicht erst unterschiedlich behandelt.

      Wird mir dann vermutlich auch nix anderes übrig bleiben. Sonst steigt da ja später keiner mehr durch. Sollte man dann für HTML-Dateien grundsätzlich "\r\n", also CRLF benutzen und für Progammdateien PHP_EOL?

      Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

      Hä?

      Die Programmdatei kann "Texte enthalten", die eigene Zeilenendezeichen enthalten. Die sollen aber ggf. (Schalter) nicht angetastet werden.

      Den Zahn werde ich aber vermutlich ziehen müssen. :-|

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bikers-lodge.com
      1. Moin!

        Besonders schwierig wird es wohl, wenn innerhalb einer Datei blöderweise auch noch unterschiedliche EOL verwendet werden.

        PHPStorm (empfehlenswerte IDE für PHP) macht es so: Das erste Zeilenendezeichen der Datei bestimmt, welche Endezeichen die Datei beim Speichern bekommt. Alle Zeilenendezeichen der Datei werden normalisiert und intern gar nicht erst unterschiedlich behandelt.

        Wird mir dann vermutlich auch nix anderes übrig bleiben. Sonst steigt da ja später keiner mehr durch. Sollte man dann für HTML-Dateien grundsätzlich "\r\n", also CRLF benutzen und für Progammdateien PHP_EOL?

        PHP_EOL ist eine systemabhängige Konstante, die unter Windows CRLF enthält, und unter Linux/MacOSX nur LF. (PHP-Versionen für klassisches MacOS kenne ich nicht, vermutlich stünde dann CR drin).

        Damit hast du dein Problem nicht gelöst.

        CRLF für Windows ist nur relevant, wenn die Bearbeiter dieser Dateien Editoren benutzen, die damit nicht klar kommen. Also im Prinzip nur der originale Windows Notepad. Alle Alternativen dazu sind in der Lage, auch LF korrekt als Zeilenende zu erkennen und umzusetzen. Auf Linux-Ebene hingegen ist das Problem mit CRLF nicht ganz so schlimm, man stört sich gelegentlich nur am angezeigten CR-Zeichen.

        Es bleibt also eine Frage der Arbeitsweise. Ich persönlich bin für überall LF benutzen. Spart immerhin ein Byte pro Zeile.

        Und innerhalb von enthaltenen Texten können dann auch noch welche vorkommen, die aber ggf. nicht mit ausgewertet und dann auch nicht ersetzt werden sollen.

        Hä?

        Die Programmdatei kann "Texte enthalten", die eigene Zeilenendezeichen enthalten. Die sollen aber ggf. (Schalter) nicht angetastet werden.

        Den Zahn werde ich aber vermutlich ziehen müssen. :-|

        Nicht umsetzbar, wenn du nicht beigehen und den Text parsen willst.

        Außerdem irrelevant. Mir ist noch kein Szenario untergekommen, in dem man den PHP-Code mit LF-Zeilenenden versieht, und darin enthaltenes HTML mit CRLF. Wenn CRLF wichtig ist, dann stehen entsprechende Zeilenenden im Code, also z.B. echo "<body>\r\n"; - Und die wirst du nicht anfassen mit der Konvertierung.

        - Sven Rautenberg

  3. hi,

    Wie müsste ein Algorithmus aussehen, der Zeilenendezeichen von Textdateien feststellen kann.

    So ähnlich wie der Algorithmus für die StringView-Library, erhältlich auf Github als stringview.js

    Dieser Algorithmus geht Byte für Byte durch den Buffer, untersucht die Wertigkeit und stellt, aufgrund des kanonischen Aufbaus des Unicode-Sytems fest, ob das entsprechende UTF-8-Zeichen zwei, drei oder vier Bytes enthält. Daraus, entweder aus 2 (1 Folgebyte) oder aus 3 (2 Folgebytes) oder aus 4 (3 Folgebytes) wird dann der Codepoint ermittelt. Der Buffer wird sozusagen vorausschauend gelesen.

    Dieser Algorithmus, auf Deine Problemstellung umgesetzt, dürfte etwas weniger kompliziert werden weil weniger Fälle betrachtet werden müssen, wenn Du Intresse hast, können wir das mal weiterhin besprechen, guck Dir mal in stringview.js die Funktion StringView.loadUTF8CharCode = function (aChars, nIdx) zur Einstimmung ;)

    Horst Fleischwurst

    --
    Wie mein Grabstein aussehen wird, weiß nur der Steinmetz.
  4. @@Tom:

    nuqneH

    Du willst [\n\r]+ ersetzen durch '\n'?

    Qapla'

    --
    „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
    1. Hi,

      Du willst [\n\r]+ ersetzen durch '\n'?

      Wohl kaum, denn das würde Leerzeilen zernichten.

      cu,
      Andreas

      --
      Warum nennt sich Andreas hier MudGuard?
      O o ostern ...
      Fachfragen per Mail sind frech, werden ignoriert. Das Forum existiert.
      1. @@MudGuard:

        nuqneH

        Du willst [\n\r]+ ersetzen durch '\n'?

        Wohl kaum, denn das würde Leerzeilen zernichten.

        Oder verstören. Zerflixt und vugenäht!

        Dann zielleicht so: '\r\n?' ersetzen durch '\n'?

        Qapla'

        --
        „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
        1. Hello,

          Du willst [\n\r]+ ersetzen durch '\n'?

          Wohl kaum, denn das würde Leerzeilen zernichten.

          Oder verstören. Zerflixt und vugenäht!

          Dann zielleicht so: '\r\n?' ersetzen durch '\n'?

          Sad kommt auf die Zielplätzchen-Form an :-))

          Für HTML soll sicherlich als Ziel "\r\n" rauskommen
          Für Linux-Shell-Umgebung (z. B. für Sendmail, was hier aber nicht unbedingt Gegensatan der Betraxung war) müsste aus "\r\n" oder "\r" -> "\n" werden. :-P

          ---
          Wobei man allerdings auch mit den Platzhaltern "\r\n" & Co. vorsichtig umgehen muss, denn sie sind eben nur Platzhalter. Gemeint war hier abber immer:

          "\r" === 0x0D
          "\n" === 0x0A
          "\r\n" === 0x0D 0x0A

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bikers-lodge.com