DanLevy.net

クイズ: NodeJS I/O マスター

ファイル、ストリーム、バッファの知識を確認しましょう

Node.js IOの世界にダイブ準備はできましたか?🌊

このクイズでは、Node.jsのIO操作に関する理解をテストします。基本的なファイルシステム操作から高度なストリーミングコンセプトまで、バッファやエンコーディング、データを効率的に処理するためのベストプラクティスについてもカバーします。

それでは、ストリームとバッファの区別がどのくらい正確にできるか試してみましょう!🚀

このコードは何を行いますか?

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

Buffer.alloc(size)は指定されたサイズでゼロで埋めた新しいバッファを作成します。 出力はこうなります: <Buffer 00 00 00 00 00>

ランダムなデータでバッファを作成したい場合はBuffer.allocUnsafe(5)を使用してください。

バッファの割り当てについて詳しく学ぶ

これは何を出力するか?

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

配列内の数字はASCIIコードを表しています:

  • 65: ‘A’

toString()はデフォルトでUTF-8エンコーディングを使ってバイトを文字列に変換します。

バッファのエンコーディングについて詳しく学ぶ

出力の順序は?

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

readFileは非同期なので、ファイルを読んでいる間にもコードは実行されます。 そのため、‘Done’がファイル内容よりも先に表示されます。

まずファイルを読了後に実行したい場合は、Promiseベースのバージョンを使うことができます:

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

デフォルトでfs.readFileSync()は何を返すか?

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

fs.readFileSync()は、エンコーディングを指定しない場合デフォルトでバッファを返します。文字列を取得するには以下のいずれかが必要です:

  1. エンコーディングを指定: fs.readFileSync('test.txt', 'utf8')
  2. バッファを変換: content.toString()

Node.jsドキュメントでfs.readFileSyncを詳しく確認

Readableストリームでよく使用されるイベントセットはどれですか?

Readableストリームはいくつか重要なイベントを発生させます:

  • ‘data’: データが読み取れるときに発生します
  • ‘end’: 読み込むデータがなくなったときに発生します
  • ‘error’: エラーが発生したときに発生します
  • ‘close’: ストリームと下位リソースが閉じたときに発生します
const readable = fs.createReadStream('file.txt');
readable.on('data', chunk => console.log(chunk));
readable.on('end', () => console.log('Done!'));

ストリームイベントについてさらに学ぶ

このコードは何を行いますか?

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

pipe()は読み込みストリームを書き込みストリームに接続し、バックプレッシャーを自動管理してデータをチャンク単位でコピーします。メモリにファイル全体を読み込むfs.readFile() + fs.writeFile()と異なり、大規模ファイル処理にメモリ効率が優れています。

pipe()について詳しく学ぶ

recursiveオプションは何をするのか?

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

recursive: trueオプションは、存在しない親ディレクトリを自動作成します。 このオプションがない場合、’./a/b/c’を作成する際に’./a’や’./a/b’が存在しないとエラーになります。

これはシェルコマンドのmkdir -pと同様の挙動です。

mkdirの詳細はこちら

このコードの出力は?

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"

トランスフォームストリームはデータが通過する際に変換します。ここでは各チャンクが以下のように処理されます:

  1. 文字列に変換
  2. 大文字に変換
  3. stdoutに送信

これにより、すべての入力を大文字に変換するパイプラインが作成されます。

トランスフォームストリームの詳細はこちら

ファイルが変更されたときにfs.watch()が保証された回数はどのくらいですか?

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

fs.watch()は論理的なファイル変更ごとに正確に1回発生することを保証されていません。多くのテキストエディタが以下のように動作するため、通常は複数回発生します:

  1. 一時ファイルに保存
  2. そのファイルをターゲットファイルにリネーム

より信頼性の高い監視には以下を検討してください:

  • chokidarパッケージ
  • コールバックのデバウンス処理
  • fs.watchFile()の使用(ただし効率が悪い)

fs.watch()の詳細はこちら

出力は?

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

バッファは値ではなく参照で比較されます。同じデータを含んでいても、異なるオブジェクトとして扱われます。

バッファのコンテンツを比較するには次を使用します:

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

バッファの比較について詳しくはこちら

ストリームのバックプレッシャーの主な目的は何ですか?

バックプレッシャーは、書き込み側が処理できないときに読み込みを一時停止することでメモリオーバーフローを防ぐメカニズムです。

手動のバックプレッシャーの例:

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

pipe()メソッドはこれを自動的に処理します!

バックプレッシャーの詳細はこちら

このコードは何を行いますか?

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

symlinkSyncはターゲットファイルへのシンボリックリンク(ショートカットのようなもの)を作成します。

ハードリンクとの主な違い:

  • ディレクトリへのリンクが可能
  • ファイルシステムをまたいでリンク可能
  • ターゲットが削除されるとリンクが破綻

代わりにハードリンクを作成するには:

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

Learn more about symlinks

Node.jsストリームはどのモードで動作できますか?

ストリームは次のように動作できます:

  1. バイナリモード(デフォルト): バッファや文字列に
  2. オブジェクトモード: 任意のJavaScript値に

オブジェクトモードの例:

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

ストリームモードについて詳しく学ぶ

このコールバックでfdパラメータの型は何ですか?

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

ファイルディスクリプタは、オペレーティングシステムで開かれたファイルを一意に識別する数値です。

最初の3つのファイルディスクリプタは予約されています:

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

必ずファイルディスクリプタを閉じることを忘れないでください:

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

ファイルディスクリプタについて詳しくはこちらnode.js.orgのドキュメント

UTF-8でこの文字列はいくつのバイトを占めますか?

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

UTF-8におけるバイト数:

  • ASCII文字(例: ‘Hello ‘)は1文字1バイト
  • 地球の絵文字🌍は4バイト

計算式: 5(Hello)+ 1(スペース)+ 4(🌍)= 10バイト

バイト数を確認するには:

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

UTF-8エンコーディングについてもっと学ぶ

Node.jsのIOに関する知識を試すことに楽しんでいただけたでしょうか?もっと挑戦したい方は、私のクイズコレクションをご覧ください。