Abschnitt 8 · Lektion 1
Eine Datei lesen
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
Rhein
Donau
Elbe <?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
Rhein
Donau
Elbe <?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_LINESschneidet den Umbruch am Zeilenende ab.FILE_SKIP_EMPTY_LINESwirft 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
<?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
Rhein
Donau
Elbe <?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
Kapitän
Schleuse
Möwe <?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, kostenlosZu 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.