mitmario.dev

Eine Datei lesen

PHP Sandbox 4 Min Lesezeit 5 BeispieleLektion 1 von 7

Eine Datei liegt auf der Festplatte, dein Programm liegt daneben, und dazwischen fehlt genau eine Zeile Code. PHP bringt dafür zwei Funktionen mit, und die Wahl zwischen ihnen ist die einzige Entscheidung, die du hier wirklich treffen musst.

Alles auf einmal: file_get_contents

Die ganze Datei in einem Rutsch
<?php

$inhalt = file_get_contents("fluesse.txt");

echo "Typ:    ", get_debug_type($inhalt), "\n";
echo "Bytes:  ", strlen($inhalt), "\n";
echo "Inhalt:\n";
echo $inhalt;
echo "[hier endet die Datei]\n";

file_get_contents() gibt dir den kompletten Inhalt als eine Zeichenkette zurück, mit allen Zeilenumbrüchen darin. Das ist der richtige Griff, wenn dich die Datei als Ganzes interessiert: ein Text, den du ausgeben willst, eine Vorlage, in die du etwas einsetzt, gleich in Lektion 8.3 eine JSON-Datei.

Sieh dir im Beispiel die Bytezahl an. Sie zählt die Zeilenumbrüche mit, denn die stehen wirklich in der Datei. Ein Zeilenumbruch ist kein Formatierungswunsch, sondern ein Zeichen wie jedes andere. Im Terminal kannst du ihn sichtbar machen: cat -A fluesse.txt zeigt an jedem Zeilenende ein $, und wc -c fluesse.txt kommt auf dieselben 17 Bytes wie strlen.

Zeilenweise: file

Zeile für Zeile statt am Stück
<?php

echo "So kommt es roh zurueck:\n";
var_dump(file("fluesse.txt"));

echo "\nUnd so aufgeraeumt:\n";
$zeilen = file("fluesse.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
var_dump($zeilen);

echo "\nDamit laesst sich zaehlen: ", count($zeilen), " Zeilen\n";

file() liest dieselbe Datei und gibt dir eine Liste zurück, einen Eintrag je Zeile. Damit bist du sofort in Abschnitt 5: count(), foreach, array_filter, alles greift.

Der erste var_dump zeigt allerdings ein Detail, über das man einmal stolpert: die Zeilenumbrüche kleben noch an den Einträgen. Rhein\n und nicht Rhein. Wer damit vergleicht, sucht lange.

Dagegen gibt es zwei Schalter, und man schreibt sie fast immer zusammen:

  • FILE_IGNORE_NEW_LINES schneidet den Umbruch am Zeilenende ab.
  • FILE_SKIP_EMPTY_LINES wirft leere Zeilen weg, etwa die eine am Dateiende.

Der senkrechte Strich dazwischen ist derselbe wie bei den JSON-Schaltern in 8.3 und heißt schlicht „und dieser auch”.

Die Datei, die es nicht gibt

Wenn es die Datei gar nicht gibt
<?php

echo "Erster Versuch, ungeprueft:\n";
$inhalt = file_get_contents("gibtsnicht.txt");
var_dump($inhalt);
echo "Das Programm laeuft trotzdem weiter.\n";

echo "\nZweiter Versuch, vorher gefragt:\n";
if (is_readable("gibtsnicht.txt")) {
    echo file_get_contents("gibtsnicht.txt");
} else {
    echo "Die Datei gibt es nicht, oder sie ist nicht lesbar.\n";
}

Fehlt die Datei, passiert genau das, was du aus Lektion 7.1 kennst, und es ist der unangenehmste Fall von allen: PHP schreibt eine Warning auf den Fehlerkanal, gibt false zurück und läuft weiter.

Die Meldung lautet PHP Warning: file_get_contents(gibtsnicht.txt): Failed to open stream: No such file or directory, und damit hat es sich.

Kein Absturz, kein Exit-Code 255. Dein Programm rechnet mit false weiter, und false benimmt sich in einer Zeichenkettenoperation wie ein leerer Text. Am Ende steht eine leere Ausgabe, auf einem Server eine leere Seite, und niemand weiß, warum.

Deshalb fragst du vorher. Zwei Funktionen stehen bereit, und sie beantworten zwei verschiedene Fragen:

  • file_exists() sagt, ob dort etwas liegt. Auch ein Verzeichnis zählt als „ja”.
  • is_readable() sagt, ob du es auch aufmachen darfst.

Für den Alltag ist is_readable() die bessere Frage, denn genau darum geht es dir ja. Und wenn ein fehlender Inhalt bedeutet, dass es nicht weitergehen kann, dann ist das der Moment für eine Ausnahme aus Lektion 7.4. Ein if und darin ein throw new RuntimeException("Die Datei fehlt: " . $pfad); sind alles, was es dafür braucht.

Relative Pfade und DIR

Wo dein Skript zu Hause ist
<?php

echo "Die Datei liegt in:      ", __DIR__, "\n";
echo "Gearbeitet wird gerade in: ", getcwd(), "\n";

echo "\nBeide Wege fuehren hier zum Ziel:\n";
echo "relativ: ", strlen(file_get_contents("fluesse.txt")), " Bytes\n";
echo "sicher:  ", strlen(file_get_contents(__DIR__ . "/fluesse.txt")), " Bytes\n";

"fluesse.txt" ist ein relativer Pfad. Die Frage ist nur: relativ wozu?

Nicht zu der Datei, in der die Zeile steht. Sondern zu dem Verzeichnis, in dem der PHP-Prozess gerade arbeitet, und das ist in der Regel das Verzeichnis, aus dem heraus du php aufgerufen hast. In diesem Kurs sind beide gleich, deshalb funktioniert der kurze Weg hier immer.

Auf einem echten Server sind sie es oft nicht, und dann bricht der kurze Weg auf eine besonders gemeine Art: er funktioniert, solange du von Hand testest, und scheitert, sobald ein Cronjob oder ein Webserver das Skript startet.

__DIR__ ist die verlässliche Antwort. Es ist keine Funktion, sondern eine magische Konstante, und PHP setzt dort beim Einlesen das Verzeichnis dieser Datei ein. Zusammengeklebt mit einem Schrägstrich ergibt das einen Pfad, der immer stimmt, egal von wo aus gestartet wird.

Und noch einmal: Zeichen sind nicht Bytes

Zeichen zählen, nicht Bytes
<?php

$zeilen = file("woerter.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

foreach ($zeilen as $wort) {
    echo str_pad($wort, 12), " strlen: ", strlen($wort);
    echo "   mb_strlen: ", mb_strlen($wort), "\n";
}

Das ist die Falle aus Lektion 3.5, jetzt mit Daten aus einer Datei. strlen() zählt Bytes, mb_strlen() zählt Zeichen, und ein Umlaut braucht in UTF-8 zwei Bytes. Auch das zeigt dir cat -A woerter.txt im Terminal: Aus dem ä in Kapitän werden dort zwei Zeichenpaare, M-CM-$, eines je Byte.

Im Beispiel siehst du die Folge nebenbei: Die Spalten stehen nicht sauber untereinander, weil str_pad ebenfalls in Bytes rechnet und die Wörter mit Umlaut deshalb zu wenig Auffüllung bekommen. Genau die Verschiebung aus Lektion 3.4.

Solange du nur Bytes vergleichst, weil du wissen willst, wie viel Platz etwas braucht, ist strlen richtig. Sobald du etwas zählst, das ein Mensch als Zeichen liest, nimm mb_strlen. Die Challenge gleich sucht die längste Zeile, und dort ist genau das der Unterschied zwischen richtig und falsch.

Zum Mitnehmen

Bis hierhin kannten deine Programme nur, was du selbst hineingeschrieben hast. Ab jetzt holen sie sich Daten von der Festplatte, und zwar mit zwei Funktionen, die sich nur in einer Kleinigkeit unterscheiden.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 5 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.