mitmario.dev

Dateien schreiben

Node.js Sandbox 3 Min Lesezeit 3 BeispieleLektion 2 von 7

Lesen war die harmlose Hälfte. Beim Schreiben veränderst du etwas, das danach so bleibt, und dabei gibt es ein paar Dinge, die man einmal wissen sollte, bevor sie einem passieren.

Zwei Funktionen, ein Unterschied

writeFile legt eine Datei an. Gibt es sie schon, ersetzt es ihren Inhalt. appendFile hängt an das an, was schon da ist, und legt die Datei an, falls es sie noch nicht gibt.

Anlegen und ergänzen
import { writeFile, appendFile, readFile } from "node:fs/promises";

await writeFile("notiz.txt", "Erste Zeile\n");
await appendFile("notiz.txt", "Zweite Zeile\n");

console.log(await readFile("notiz.txt", "utf8"));

Achte auf das \n am Ende der Texte. Node schreibt genau das, was du übergibst, und hängt von sich aus keinen Zeilenumbruch an. Ohne die beiden \n stünde in der Datei Erste ZeileZweite Zeile in einer einzigen Zeile.

Nachsehen, statt zu glauben

Bis hierhin hast du jeder Ausgabe geglaubt, denn etwas anderes ging nicht. Ab dieser Lektion geht etwas anderes, und das ist das Terminal unter dem Editor. Es läuft in demselben Verzeichnis wie dein Programm, auf derselben Maschine, und es zeigt dir den Stand, den der letzte Lauf hinterlassen hat.

Starte die Sandbox, dann läuft das Beispiel oben, und tipp danach ls. Neben index.mjs steht jetzt notiz.txt, und cat notiz.txt gibt dir die beiden Zeilen zurück. Der Unterschied zum console.log im Beispiel ist klein und trotzdem entscheidend: Dort zeigt dir dein eigenes Programm, was es gelesen zu haben glaubt. Hier siehst du die Datei.

Eins gehört dazu: Der Prüfknopf räumt das Verzeichnis vorher auf, das Terminal nicht. Was du dort siehst, ist also der Stand nach dem letzten Lauf, und nach einem Klick auf Prüfen fängt alles wieder mit den Dateien aus dem Editor an.

Es gibt keine Rückfrage

Das ist der Punkt, den ich am Anfang meiner Laufbahn einmal auf die harte Tour gelernt habe.

writeFile fragt nicht nach
import { writeFile, readFile } from "node:fs/promises";

await writeFile("notiz.txt", "Der wichtige Text von gestern\n");
console.log("Vorher:", (await readFile("notiz.txt", "utf8")).trim());

await writeFile("notiz.txt", "Neu\n");
console.log("Nachher:", (await readFile("notiz.txt", "utf8")).trim());

Der alte Inhalt ist weg. Nicht im Papierkorb, nicht in einer .bak-Datei, sondern weg. Es gibt keine Warnung, keinen Rückgabewert, der dich darauf hinweist, und keinen Weg zurück.

Das ist kein Konstruktionsfehler, sondern genau das, was der Name verspricht. Aber es heißt: Bevor du writeFile auf einen Pfad ansetzt, sei dir sicher, welche Datei dort liegt. Besonders dann, wenn der Pfad aus einer Variablen kommt und nicht direkt im Code steht.

Wer wirklich nur anlegen und niemals überschreiben will, kann writeFile mit { flag: "wx" } aufrufen. Dann scheitert der Aufruf, wenn die Datei schon existiert, statt sie zu ersetzen.

Ordner legt Node nicht nebenbei an

Der zweite Stolperstein, und er sieht auf den ersten Blick nach einem Fehler in Node aus.

Ein fehlender Ordner ist ein Fehler
import { writeFile } from "node:fs/promises";

// Den Ordner berichte gibt es nicht, und Node legt ihn nicht nebenbei an.
await writeFile("berichte/juli.txt", "Inhalt\n");

Du willst eine Datei anlegen, und Node sagt, dass etwas nicht existiert. Gemeint ist nicht deine Datei, sondern der Ordner darüber. writeFile legt genau eine Sache an, nämlich die Datei, und verlangt, dass der Weg dorthin schon da ist.

Merk dir ENOENT. Der Code steht für „error: no entry” und begegnet dir in Node ständig. Er heißt immer dasselbe: An diesem Pfad ist nichts. Wie du Ordner anlegst, kommt in der nächsten Lektion, und wie du solche Fehler abfängst, statt daran abzustürzen, in Lektion 5 dieses Abschnitts.

Umbenennen und löschen

Zwei weitere Funktionen aus node:fs/promises, die du kennen solltest.

rename(alt, neu) benennt um und verschiebt zugleich, denn ein neuer Pfad ist ein neuer Ort. Es ist die übliche Art, sicher zu speichern: erst in eine temporäre Datei schreiben, dann umbenennen. Bricht das Programm mittendrin ab, ist die alte Datei noch vollständig da statt halb überschrieben.

rm(pfad) löscht. Mit { recursive: true } löscht es einen Ordner samt allem, was darin liegt, und genau das tut es dann auch. Es gibt keine Sicherheitsabfrage. Wenn der Pfad aus einer Variablen kommt, schau dir vorher an, was wirklich darin steht.

Nichts davon ist kompliziert. Das Unangenehme an diesen Funktionen ist nicht ihre Bedienung, sondern dass sie genau das tun, was du sagst, und nicht das, was du meinst.

Zum Mitnehmen

writeFile überschreibt kommentarlos. Es gibt keine Rückfrage, keine Sicherungskopie und keinen Papierkorb.

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 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.