Aufwärmen · Aufgabe 1 von 7
// B3.2 · ca. 34 Min. · Einstieg
Dateien lesen mit fs/promises
Nach dieser Lektion lesen Sie Text- und JSON-Dateien, unterscheiden eine fehlende Datei von einem Ordner oder einer gesperrten Datei und finden eine Datei neben Ihrem Modul.
Danach können Sie
- Eine Datei als Buffer oder String lesen und ihren Text in JSON oder Zeilen umwandeln
- ENOENT, EISDIR und EACCES per error.code behandeln und den Rest weiterwerfen
- Promise-, Callback- und Sync-Form vergleichen und Pfade relativ zu import.meta.dirname bilden
Vorhersagen · Aufgabe 2 von 7
Sagen Sie es vorher, bevor Sie weiterlesen. hello.txt enthält die Zeile Hi. Was gibt dieses Programm aus?
import {readFile} from 'node:fs/promises'; const data = await readFile('hello.txt'); console.log(data);Üben · Aufgabe 3 von 7
Setzen Sie die Kodierung ein, damit readFile einen String liefert und das Programm string buy milk ausgibt.
const text = await readFile("notes.txt", "____"); console.log(typeof text, text.trim());const text = await readFile("notes.txt", "");Üben · Aufgabe 4 von 7
Ihr Programm liest eine Datei und wertet sie als JSON aus. Ordnen Sie jedem Fehler die Situation zu, die ihn auslöst.
Üben · Aufgabe 5 von 7
In welcher Reihenfolge gibt dieses Programm seine drei Zeilen aus?
import {readFileSync} from 'node:fs'; import {readFile} from 'node:fs/promises'; readFile('names.txt', 'utf8').then(() => console.log('promise')); readFileSync('names.txt', 'utf8'); console.log('sync'); console.log('end');Denksport · Aufgabe 6 von 7
Knobelaufgabe. Der Ordner app enthält main.js und data.txt mit dem Inhalt hello from app. Sie stehen im Ordner über app und führen node app/main.js aus. Was passiert?
// app/main.js import {readFile} from 'node:fs/promises'; const text = await readFile('data.txt', 'utf8'); console.log(text.trim());Anwenden · Aufgabe 7 von 7
Mini-Aufgabe. Legen Sie einen Ordner app mit config.json ({"port": 8080}) und einer main.js an, die config.json neben sich liest und port 8080 ausgibt. Starten Sie sie aus dem Ordner darüber mit node app/main.js. Benennen Sie config.json dann um und ersetzen Sie sie später durch einen gleichnamigen Ordner: Jedes Mal muss das Programm eine klare Zeile auf stderr ausgeben und mit Exit-Code 1 enden, ohne Stacktrace.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Eine Aufgabenliste und drei Arten, wie Lesen scheitern kann
Dieses Programm liest tasks.txt aus dem Ordner von main.js, zerlegt sie in Zeilen und überspringt die leere. Dann versucht es eine Datei, die es nicht gibt, und den Ordner selbst und macht aus jedem error.code einen kurzen Satz; ein unerwarteter Code würde weitergeworfen. Zuletzt liest es tasks.txt ohne Kodierung, um zu zeigen, dass Sie dann Bytes erhalten. Weil die Pfade bei import.meta.dirname beginnen, ist die Ausgabe gleich, egal von wo Sie es starten.
main.js
import {readFile} from 'node:fs/promises';
import {join} from 'node:path';
const here = import.meta.dirname; // the folder of main.js
async function show(name) {
try {
const text = await readFile(join(here, name), 'utf8');
const lines = text.split(/\r?\n/).filter((line) => line.trim() !== '');
console.log(`${name}: ${lines.length} tasks`);
for (const line of lines) console.log(' -', line);
} catch (error) {
const why = {ENOENT: 'does not exist', EISDIR: 'is a folder', EACCES: 'may not be read'}[error.code];
if (!why) throw error; // not one we expected: let it crash
console.log(`${name}: ${why} (${error.code})`);
}
}
await show('tasks.txt');
await show('missing.txt');
await show('.');
const bytes = await readFile(join(here, 'tasks.txt'));
console.log('without an encoding:', bytes.length, 'bytes', bytes.subarray(0, 3));
tasks.txt
buy milk
water plants
call Ada
Ausführen mit
node main.jsAusgabe
tasks.txt: 3 tasks
- buy milk
- water plants
- call Ada
missing.txt: does not exist (ENOENT)
.: is a folder (EISDIR)
without an encoding: 32 bytes <Buffer 62 75 79>- Die leere Zeile zwischen buy milk und water plants und das leere Element nach dem letzten Zeilenumbruch werden herausgefiltert.
- join(here, '.') ist der Ordner von main.js selbst, also lehnt readFile mit EISDIR ab.
- EACCES steht in der Liste, aber hier ist keine Datei gesperrt, also taucht es nie auf.
- Ohne "utf8" liefert readFile einen Buffer: 62 75 79 sind die Bytes von b, u und y.
Übungen
Übung 1 von 2
Vom Dateitext zu Datensätzen
Ein Programm liest scores.txt mit readFile(path, "utf8") und übergibt den Text an parseScores(text). Jede Zeile lautet name,score. Geben Sie ein Array von {name, score}-Objekten zurück, mit score als Zahl. Überspringen Sie leere Zeilen, auch das leere Element nach dem letzten Zeilenumbruch, akzeptieren Sie Windows-Zeilenenden (\r\n) und entfernen Sie Leerzeichen um beide Teile. Dieser Teil braucht gar keine Datei, er läuft also auch im Browser.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
text.split(/\r?\n/) schneidet an \n und an \r\n gleichermaßen.
Hinweis 2
filter((line) => line.trim() !== "") entfernt leere Zeilen und das leere letzte Element.
Hinweis 3
line.split(",") ergibt [name, score]; kürzen Sie den Namen mit trim, und Number(" 90") ist 90.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// Turns the text of scores.txt (one "name,score" per line) into records.
// readFile(path, "utf8") gives this text; parsing it needs no file.
export function parseScores(text) {
return text
.split(/\r?\n/)
.filter((line) => line.trim() !== "")
.map((line) => {
const [name, score] = line.split(",");
return {name: name.trim(), score: Number(score)};
});
}
console.log(parseScores("Ada,90\nGrace,85\n"));
Auf dem eigenen Computer ausführen
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// Turns the text of scores.txt (one "name,score" per line) into records.
// readFile(path, "utf8") gives this text; parsing it needs no file.
export function parseScores(text) {
return [];
}
console.log(parseScores("Ada,90\nGrace,85\n"));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {parseScores} from './main.js';
test('zwei Zeilen mit abschließendem Zeilenumbruch ergeben zwei Datensätze', () => {
const got = parseScores('Ada,90\nGrace,85\n');
assert.deepEqual(got, [{name: 'Ada', score: 90}, {name: 'Grace', score: 85}], `parseScores lieferte ${JSON.stringify(got)}`);
});
test('leere Zeilen werden übersprungen', () => {
const got = parseScores('Ada,90\n\n\nGrace,85');
assert.equal(got.length, 2, `parseScores lieferte ${JSON.stringify(got)}`);
});
test('Windows-Zeilenenden und Leerzeichen werden behandelt', () => {
const got = parseScores(' Ada , 90\r\nGrace,85\r\n');
assert.deepEqual(got, [{name: 'Ada', score: 90}, {name: 'Grace', score: 85}], `parseScores lieferte ${JSON.stringify(got)}`);
});
test('eine leere Datei ergibt ein leeres Array', () => {
assert.deepEqual(parseScores(''), [], `parseScores("") lieferte ${JSON.stringify(parseScores(''))}`);
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 2 von 2
Klare Meldungen für eine Einstellungsdatei
loadSettings(name) liest die JSON-Datei name aus dem Ordner von main.js und gibt den ausgewerteten Wert zurück. Lassen Sie jedes erwartete Problem mit einem Error ablehnen, dessen message eine klare Zeile ist: "missing.json: no such file" bei ENOENT, ".: is a folder, not a file" bei EISDIR (hier war der Name "."), und "broken.json: not valid JSON", wenn JSON.parse scheitert. Jeden anderen Fehler werfen Sie unverändert weiter. Starten Sie node main.js und prüfen Sie mit node --test.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Hinweise
Hinweis 1
Setzen Sie nur den readFile-Aufruf in ein eigenes try und JSON.parse in ein zweites, dann wissen Sie, welcher Schritt scheiterte.
Hinweis 2
Vergleichen Sie im ersten catch error.code mit 'ENOENT' und 'EISDIR', und enden Sie für den Rest mit throw error;.
Hinweis 3
throw new Error(`${name}: no such file`) lehnt die async-Funktion mit dieser Meldung ab.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import {readFile} from 'node:fs/promises';
import {join} from 'node:path';
// Reads a JSON file next to main.js and returns the parsed value.
export async function loadSettings(name) {
let text;
try {
text = await readFile(join(import.meta.dirname, name), 'utf8');
} catch (error) {
if (error.code === 'ENOENT') throw new Error(`${name}: no such file`);
if (error.code === 'EISDIR') throw new Error(`${name}: is a folder, not a file`);
throw error;
}
try {
return JSON.parse(text);
} catch {
throw new Error(`${name}: not valid JSON`);
}
}
try {
console.log(await loadSettings('settings.json'));
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
Auf dem eigenen Computer ausführen
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
import {readFile} from 'node:fs/promises';
import {join} from 'node:path';
// Reads a JSON file next to main.js and returns the parsed value.
export async function loadSettings(name) {
const text = await readFile(join(import.meta.dirname, name), 'utf8');
return JSON.parse(text);
}
try {
console.log(await loadSettings('settings.json'));
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadSettings} from './main.js';
test('liest settings.json und wertet sie aus', async () => {
const got = await loadSettings('settings.json');
assert.deepEqual(got, {theme: 'dark', fontSize: 14}, `loadSettings lieferte ${JSON.stringify(got)}`);
});
test('eine fehlende Datei ergibt eine klare Meldung', async () => {
await assert.rejects(loadSettings('missing.json'), {message: 'missing.json: no such file'}, 'erwartet war die Meldung "missing.json: no such file"');
});
test('ein Ordner ergibt eine klare Meldung', async () => {
await assert.rejects(loadSettings('.'), {message: '.: is a folder, not a file'}, 'erwartet war die Meldung ".: is a folder, not a file"');
});
test('fehlerhaftes JSON ergibt eine klare Meldung', async () => {
await assert.rejects(loadSettings('broken.json'), {message: 'broken.json: not valid JSON'}, 'erwartet war die Meldung "broken.json: not valid JSON"');
});
settings.json
{"theme": "dark", "fontSize": 14}
broken.json
{theme: "dark"}
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenHäufige Fehler
Das Promise statt des Texts auswerten
import {readFile} from 'node:fs/promises';
const settings = JSON.parse(readFile('settings.json', 'utf8'));
console.log(settings.theme);
Was Node.js ausgibt
SyntaxError: Unexpected token 'o', "[object Promise]" is not valid JSONWarum, und die Lösung
readFile aus node:fs/promises gibt ein Promise zurück, nicht den Text. JSON.parse wandelt sein Argument zuerst in einen String um, das ergibt "[object Promise]", und das ist kein JSON. Warten Sie das Lesen ab: JSON.parse(await readFile("settings.json", "utf8")).
Einen Buffer wie einen String behandeln
import {readFile} from 'node:fs/promises';
const text = await readFile('names.txt');
console.log(text.split('\n'));
Was Node.js ausgibt
TypeError: text.split is not a functionWarum, und die Lösung
Ohne Kodierung liefert readFile einen Buffer aus Bytes, und ein Buffer hat keine Methode split. Verlangen Sie beim Lesen Text: await readFile("names.txt", "utf8"). Dann funktionieren split, trim und die anderen String-Methoden.
err im Callback ignorieren
import {readFile} from 'node:fs';
readFile('setings.json', 'utf8', (err, text) => {
console.log(text.trim());
});
Was Node.js ausgibt
TypeError: Cannot read properties of undefined (reading 'trim')Warum, und die Lösung
Die Callback-Form übergibt zuerst den Fehler. Hier ist der Name falsch geschrieben, also ist err ein ENOENT-Fehler und text undefined, und der Absturz verdeckt die eigentliche Ursache. Prüfen Sie err, bevor Sie die Daten nutzen: if (err) { console.error(err.code); return; }. Oder nehmen Sie die Promise-Form mit try/catch.
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.