Enrico: Problem beim Einlesen einer Textdatei

Hallo und guten Abend,

ich arbeite bei unserem Gästebuch mit Textdateien.

Die Textdateien bestehen dabei aus einzelnen Zeilen, die ich mittels des Kennzeichens (?) "PHP_EOL" trenne, die einzelnen Informationen der Zeilen wiederum sind durch "|" voneinander abgegrenzt.

Hier der php-Code zum Einlesen der Einträge:

$Datei  = "../Textdateien/Gaestebuch__Eintraege.txt";
   $Dateiname = substr ($Datei, 15);

if (file_exists ($Datei))
   {
      if (is_readable ($Datei))
      {
         $Zeiger = @fopen ($Datei, "rb");

if ($Zeiger)
         {
            fseek ($Zeiger, 0, SEEK_END);
            $Dateigroesse = ftell ($Zeiger);

if ($Dateigroesse > 0)
            {
               $Versuche = 0;

do
               {
                  if ($Versuche > 0)
                  {
                     usleep (rand (1, 10000));
                  }

$Versuche += 1;
               }
               while (! flock ($Zeiger, LOCK_EX) and $Versuche <= 100);

if ($Versuche === 100)
               {
                  $Meldung = 'Die Datei "' . $Dateiname . '" kann nicht verriegelt werden';
               }
               else
               {
                  echo "Lese Datei<br><br>";

$Inhalt = @fread ($Zeiger, $Dateigroesse);

flock  ($Zeiger, LOCK_UN);
                  fclose ($Zeiger);
                  echo "<br><br>Datei eingelesen";

var_dump ($Inhalt);
               }
            }
         }
      }
   }

Die Ausgabe im Browser liefert mir folgende Angaben:

Dateigroesse: 2597
   filesize:     2597
   Lese Datei
   string(0) ""
   Datei eingelesen

D.h., folgende Voraussetzungen scheinen erfüllt zu sein:

1. Datei besteht
2. Datei ist lesbar
3. Zeiger kann initialisiert werden
4. Dateigröße > 0, d.h. es wird fortgefahren
5. Verriegelung klappt

Was ich allerdings nicht verstehe, ist, dass $Inhalt leer ist.

Ich habe etliche Seiten im Internet durchforstet und verschiedene Ansätze ausprobiert, aber das Array "$Inhalt" bekommt keinen Inhalt zugewiesen.

Was habe ich falsch gemacht?

Vielen Dank für eure Mithilfe.

Gruß,
Enrico

  1. Hallo,

    um die Dateigrösse festzustellen, bewegst du den Schreib/Lese-Poiner zuerst an das Ende der Datei (mittels "fseek"). Die nachfolgende "fread"-Funktion (mit der du versuchst, "$Inhalt" einzulesen) liest ab der aktuellen Position - die nun ja am Ende der Datei steht, und entspr. keine weiteren Daten vorfindet. Führe also vorher ein entspr. "fseek" an den Anfang der Datei aus.

    Gruss,
    Worf

    1. Hallo Worf,

      perfekt, das wars !! :-)

      Vielen Dank für Deine Hilfe.

      Gruß
      Enrico

      1. Hallo Enrico,

        gerne.
        Trotzdem würde ich den Hinweis von Kai beachten und die Daten entspr. der CSV-Formatierung (d.h. die einzelnen Felder z.B. in doppelte Anführungszeichen setzen) sichern (was automatisch dazu führt, dass du die getcsv-Funktion nutzen kannst): Was passiert z.B. bei der von dir ausgedachten Methode, wenn jemand das von dir verwendete Trennzeichen in einem der Felder eingibt?

        Gruss,
        Worf

  2. [latex]Mae  govannen![/latex]

    Die Textdateien bestehen dabei aus einzelnen Zeilen, die ich mittels des Kennzeichens (?) "PHP_EOL" trenne, die einzelnen Informationen der Zeilen wiederum sind durch "|" voneinander abgegrenzt.

    Kannst du nicht fgetcsv() mit entsprechenden Parametern benutzen?

    Stur lächeln und winken, Männer!
    Kai

    --
    Dank Hixies Idiotenbande geschieht grade eben wieder ein Umdenken
    in Richtung "Mess up the Web".(suit)
    SelfHTML-Forum-Stylesheet
  3. Moin!

    ich arbeite bei unserem Gästebuch mit Textdateien.

    Dazu gibts diverses zu kommentieren.

    Die Textdateien bestehen dabei aus einzelnen Zeilen, die ich mittels des Kennzeichens (?) "PHP_EOL" trenne, die einzelnen Informationen der Zeilen wiederum sind durch "|" voneinander abgegrenzt.

    PHP_EOL ist eine plattformabhängige Konstante, die das gebräuchliche Zeilenendezeichen enthält. Damit würde ich NICHT arbeiten, denn es sorgt dafür, dass deine Datei plattformabhängig wird, und wenn du das gesamte Skript auf eine andere Plattform kopierst, funktioniert es nicht mehr korrekt. Verwende immer feste Zeichen zur Kennzeichnung von wichtigen Dateiformatierungen. Auch unter Windows funktioniert das Unix-Zeilenende "LF" prima.

    Hier der php-Code zum Einlesen der Einträge:

    Der Code macht ziemlich viel Brimborium, ohne dass sich dafür direkt ein Sinn ergibt.

    Und er macht Dinge falsch.

    $Datei  = "../Textdateien/Gaestebuch__Eintraege.txt";
       $Dateiname = substr ($Datei, 15);

    Funktion basename() existiert. Dann muss man nicht abzählen, wie lang der Pfad in der Variablen ist.

    if (file_exists ($Datei))
       {
          if (is_readable ($Datei))

    is_readable() enthält die Abfrage für file_exists() schon - das muss man nicht doppelt tun, spart dir eine Einrückungsebene ein.

    {
             $Zeiger = @fopen ($Datei, "rb");

    An dieser Stelle die Fehler zu unterdrücken ist eventuell keine so schlaue Idee. Schließlich hast du ja zahlreiche Testfunktionen drumherum genau deswegen, dass Fehler abfangbar werden.

    Und das Locking macht man, wenn man es benötigt, sofort nach dem Dateiöffnen!

    if ($Zeiger)
             {
                fseek ($Zeiger, 0, SEEK_END);
                $Dateigroesse = ftell ($Zeiger);

    if ($Dateigroesse > 0)
                {
                   $Versuche = 0;

    do
                   {
                      if ($Versuche > 0)
                      {
                         usleep (rand (1, 10000));
                      }

    $Versuche += 1;
                   }
                   while (! flock ($Zeiger, LOCK_EX) and $Versuche <= 100);

    Und nicht erst Zahlreiche Millisekunden und eine unbekannte Verzögerungszeit später.

    Abgesehen davon ist diese Schleife hier sowieso überflüssig, weil du kein nicht-lockendes flock() aufrufst.

    if ($Versuche === 100)

    Wenn du dich mit der Zahl 100 und der Schleife und den Bedingungen verrechnet hast, könnte der Fall eintreten, dass $Versuche nicht === 100 ist, aber die do/while-Schleife dort oben aufgegeben hat. Nicht gut!

    flock() gibt den Erfolg als Funktionsergebnis zurück. Zähle nicht mit, sondern werte diesen Wert aus.

    {
                      $Meldung = 'Die Datei "' . $Dateiname . '" kann nicht verriegelt werden';
                   }
                   else
                   {
                      echo "Lese Datei<br><br>";

    $Inhalt = @fread ($Zeiger, $Dateigroesse);

    flock  ($Zeiger, LOCK_UN);
                      fclose ($Zeiger);

    Locking-Tutorial, erste Seite, erster Absatz: Niemals eine Datei vor dem fclose() entsperren. fclose() macht das von allein, und genau zu dem richtigen Zeitpunkt dann, wenn alle Puffer auf Platte geschrieben wurden etc. Wenn du das selbst machen würdest, könnte immer noch ungünstiges Zeugs passieren.

    echo "<br><br>Datei eingelesen";

    var_dump ($Inhalt);
                   }
                }
             }
          }
       }

    Abgesehen davon: Wenn du in diesem Skript nur die Gästebuchdatei lesen willst, würde ich persönlich gar keinen großen Aufwand mit Locking betreiben. file_get_contents() funktioniert viel einfacher.

    - Sven Rautenberg

    1. Hello Sven,

      {
               $Zeiger = @fopen ($Datei, "rb");

      An dieser Stelle die Fehler zu unterdrücken ist eventuell keine so schlaue Idee. Schließlich hast du ja zahlreiche Testfunktionen drumherum genau deswegen, dass Fehler abfangbar werden.

      Das verstehe ich jetzt micht. Er wertet den Fehler doch selber aus (wenn auch nicht substantiiert) und schaltet nur explizit an dieser Stelle die Fehlermeldung aus. Das kann man zwar auch geschickter nachher im Produktivbetrieb durch Setzen eines bestimmten Error-Reportings tun, aber was ist jetzt hier wirklich falsch?

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Moin!

        $Zeiger = @fopen ($Datei, "rb");

        An dieser Stelle die Fehler zu unterdrücken ist eventuell keine so schlaue Idee. Schließlich hast du ja zahlreiche Testfunktionen drumherum genau deswegen, dass Fehler abfangbar werden.

        Das verstehe ich jetzt micht. Er wertet den Fehler doch selber aus (wenn auch nicht substantiiert) und schaltet nur explizit an dieser Stelle die Fehlermeldung aus. Das kann man zwar auch geschickter nachher im Produktivbetrieb durch Setzen eines bestimmten Error-Reportings tun, aber was ist jetzt hier wirklich falsch?

        Lies meine Anmerkung nochmal. Von "falsch" steht da nix.

        - Sven Rautenberg

    2. Hallo Sven,

      danke für Deine ausführliche Stellungnahme zu den einzelnen Tests etc. und Deine Verbesserungsanregungen, sehr hilfreich.

      Werde ich umsetzen.

      Gruß
      Enrico

    3. Nochmals Hallo Sven,

      habe Deine Punkte soweit umgesetzt, was ich allerdings nicht verstehe, ist, was Du unter "kein nicht-lockendes flock()" verstehst.

      Was ist (im Umkehrschluss) ein lockendes flock()?

      Gruß
      Enrico

      1. Hello,

        habe Deine Punkte soweit umgesetzt, was ich allerdings nicht verstehe, ist, was Du unter "kein nicht-lockendes flock()" verstehst.

        Sven meint ein nicht blockierndes flock().

        Gemeint ist damit, dass beim Lock-Versuch eines "normalen" flock() der Programmfluss an dieser Stelle so lange angehalten wird, bis der Lock-Versuch entweder Erfolg hat, oder aus einem anderen Grund als einer Locking-Konkurrenz scheitert.

        Hierzu ist der Bit-Parameter LOCK_NB in der Funktion vorhanden.
        http://de.php.net/manual/en/function.flock.php

        5 Versuche mit einer geringen Verzögerungszeit (8ms) reichen dann auch i.d.R. Danach wäre eine Benutzer-Interaktion besser.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hallo Tom,

          aha, ok, jetzt weiß ich hier Bescheid.

          Danke Dir :-)

          Gruß
          Enrico