DanLevy.net

Quiz: NodeJS‑IO‑Meisterschaft

Testen Sie Ihr Wissen zu Dateien, Streams und Buffern

Bereit, in die Welt von NodeJS IO einzutauchen? 🌊

Dieses Quiz prüft Ihr Verständnis der IO‑Operationen von Node, von einfachen Dateisystem‑Aufrufen bis hin zu fortgeschrittenen Streaming‑Konzepte. Wir behandeln Buffer, Kodierung und bewährte Vorgehensweisen für eine effiziente Datenverarbeitung.

Mal sehen, wie gut Sie Ihre Streams im Vergleich zu Ihren Buffern kennen! 🚀

Was macht dieser Code?

const buf = Buffer.alloc(5);
console.log(buf);

Buffer.alloc(size) erstellt einen neuen Buffer mit der angegebenen Größe, gefüllt mit Nullen. Die Ausgabe ist: <Buffer 00 00 00 00 00>

Wenn du einen Buffer mit zufälligen Daten erstellen willst, benutze Buffer.allocUnsafe(5).

Erfahre mehr über Buffer‑Allocation

Was wird das ausgeben?

const buf = Buffer.from([65]);
console.log(buf.toString());

Die Zahlen im Array repräsentieren ASCII‑Codes:

  • 65: ‘A’

toString() wandelt diese Bytes standardmäßig mithilfe der UTF‑8‑Kodierung in ihre String‑Darstellung um.

Erfahren Sie mehr über Buffer‑Kodierung

Wie lautet die Ausgabereihenfolge?

import fs from 'fs';
fs.readFile('test.txt', 'utf8', (err, data) => {
console.log(data);
});
console.log('Done');

Da readFile asynchron ist, wird der Code weiter ausgeführt, während die Datei gelesen wird. Deshalb wird “Done” vor dem Dateinhalt ausgegeben.

Um zuerst auf das Lesen der Datei zu warten, könntest du die Promise-basierte Version verwenden:

import { promises as fs } from 'fs';
async function read() {
const data = await fs.readFile('test.txt', 'utf8');
console.log(data);
console.log('Done');
}

Was gibt fs.readFileSync() standardmäßig zurück?

import fs from 'fs';
const content = fs.readFileSync('test.txt');

fs.readFileSync() gibt standardmäßig einen Buffer zurück, wenn keine Kodierung angegeben ist. Wenn du einen String möchtest, musst du entweder:

  1. Eine Kodierung angeben: fs.readFileSync('test.txt', 'utf8')
  2. Den Buffer konvertieren: content.toString()

Mehr erfahren über fs.readFileSync in den Node.js‑Docs

Welche Menge von Ereignissen wird üblicherweise mit lesbaren Streams verwendet?

Lesbare Streams geben mehrere wichtige Ereignisse aus:

  • ‘data’: Wenn Daten zum Lesen verfügbar sind
  • ‘end’: Wenn keine Daten mehr zum Lesen vorhanden sind
  • ‘error’: Wenn ein Fehler auftritt
  • ‘close’: Wenn der Stream und die zugrunde liegende Ressource geschlossen wurden
const readable = fs.createReadStream('file.txt');
readable.on('data', chunk => console.log(chunk));
readable.on('end', () => console.log('Done!'));

Mehr über Stream‑Ereignisse erfahren

Was macht dieser Code?

import fs from 'fs';
const readable = fs.createReadStream('source.txt');
const writable = fs.createWriteStream('dest.txt');
readable.pipe(writable);

pipe() verbindet einen lesbaren Stream mit einem schreibbaren Stream, verwaltet automatisch den Backpressure und kopiert Daten in Stücke, ohne die gesamte Datei in den Speicher zu laden.

Das ist speichereffizient für große Dateien im Vergleich zu fs.readFile() gefolgt von fs.writeFile().

Mehr erfahren über pipe()

Was bewirkt die Option recursive?

import fs from 'fs';
fs.mkdirSync('./a/b/c', { recursive: true });

Die Option recursive: true erstellt übergeordnete Verzeichnisse, falls sie nicht existieren. Ohne diese Option würde der Versuch, ’./a/b/c’ zu erstellen, einen Fehler werfen, wenn ’./a’ oder ’./a/b’ nicht existieren.

Das ist ähnlich dem Shell‑Befehl mkdir -p.

Mehr erfahren über mkdir

Was wird das ausgeben?

import { Transform } from 'stream';
const upperCase = new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString().toUpperCase());
}
});
process.stdin
.pipe(upperCase)
.pipe(process.stdout);
// Input: "hello world"

Transform-Streams verändern Daten, während sie hindurchfließen. Hier wird jedes Chunk:

  1. In einen String konvertiert
  2. In Großbuchstaben umgewandelt
  3. An stdout weitergeleitet

Das erzeugt eine Pipeline, die die gesamte Eingabe in Großbuchstaben umwandelt.

Mehr über Transform-Streams erfahren

Wie oft wird fs.watch() garantiert ausgelöst, wenn eine Datei geändert wird?

import fs from 'fs';
fs.watch('test.txt', (eventType, filename) => {
console.log(`${filename} was changed`);
});
// Then modify test.txt once

fs.watch() ist nicht garantiert, exakt einmal pro logischer Dateiveränderung auszulösen. Es wird häufig mehrfach ausgelöst, weil viele Texteditoren:

  1. In eine temporäre Datei speichern
  2. Sie in die Zieldatei umbenennen

Für zuverlässigeres Beobachten solltest du erwägen:

  • Das chokidar‑Paket
  • Das Debouncen des Callbacks
  • fs.watchFile() zu verwenden (obwohl es weniger effizient ist)

Erfahre mehr über fs.watch()

Was ist die Ausgabe?

const buf1 = Buffer.from('Hello');
const buf2 = Buffer.from('Hello');
console.log(buf1 === buf2);

Buffer werden per Referenz verglichen, nicht nach Wert. Obwohl sie dieselben Daten enthalten, sind sie unterschiedliche Objekte.

Um Buffer-Inhalte zu vergleichen, verwende:

buf1.equals(buf2) // true
// or
Buffer.compare(buf1, buf2) === 0 // true

Erfahren Sie mehr über den Buffer-Vergleich

Was ist der Hauptzweck von Stream-Backpressure?

Backpressure ist ein Mechanismus, der Speicherüberlauf verhindert, indem er das Lesen pausiert, wenn das Schreibende nicht mithalten kann.

Beispiel für manuelle Backpressure:

readable.on('data', (chunk) => {
const canContinue = writable.write(chunk);
if (!canContinue) {
readable.pause();
writable.once('drain', () => readable.resume());
}
});

pipe() erledigt das automatisch!

Mehr erfahren über Backpressure

Was macht dieser Code?

import fs from 'fs';
fs.symlinkSync('target.txt', 'link.txt');

symlinkSync erstellt einen symbolischen Link (wie eine Verknüpfung) zur Zieldatei.

Wichtigste Unterschiede zu Hardlinks:

  • Kann auf Verzeichnisse verlinken
  • Kann Dateisysteme übergreifen
  • Bricht, wenn das Ziel gelöscht wird

So erstellst du stattdessen einen Hardlink:

fs.linkSync('target.txt', 'hardlink.txt');

Mehr über Symlinks erfahren

In welchen Modi können Node.js‑Streams arbeiten?

Streams können arbeiten in:

  1. Binärmodus (Standard): für Buffer und Strings
  2. Objektmodus: für beliebige JavaScript‑Werte

Beispiel für Objektmodus:

import { Transform } from 'stream';
const objectStream = new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
callback(null, { value: chunk });
}
});

Mehr über Stream‑Modi erfahren

Welchen Typ hat der fd‑Parameter in diesem Callback?

import fs from 'fs';
fs.open('test.txt', 'r', (err, fd) => {
console.log(typeof fd);
});

Dateideskriptoren sind Zahlen, die geöffnete Dateien im Betriebssystem eindeutig identifizieren.

Die ersten drei Dateideskriptoren sind reserviert:

  • 0: stdin
  • 1: stdout
  • 2: stderr

Denk immer daran, Dateideskriptoren zu schließen:

fs.close(fd, (err) => {
if (err) throw err;
});

Mehr über Dateideskriptoren erfahren

Wie viele Bytes benötigt dieser String in UTF‑8?

const str = "Hello 🌍";
const buf = Buffer.from(str);
console.log(buf.length);

In UTF‑8:

  • ASCII‑Zeichen (wie ‘Hello ’) benötigen jeweils 1 Byte
  • Das Erde‑Emoji 🌍 benötigt 4 Bytes

Also: 5 (Hello) + 1 (Leerzeichen) + 4 (🌍) = 10 Bytes

Um die Bytes zu sehen:

console.log(buf); // <Buffer 48 65 6c 6c 6f 20 f0 9f 8c 8d>

Mehr über UTF‑8‑Kodierung erfahren

Ich hoffe,Ihnen hat das Testen Ihres NodeJS‑IO‑Wissens gefallen! Noch mehr? Schauen Sie sich meine Quiz‑Sammlung für weitere Aufgaben an!