DanLevy.net

测验:NodeJS IO 精通

测试你对文件、流与缓冲区的掌握

准备好深入 NodeJS IO 的世界了吗?🌊

这个测验将测试你对 Node 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编码将这些字节转换为字符串表示。

了解更多关于Buffer编码的信息

输出顺序是什么?

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() 返回一个 Buffer。如果你想要一个字符串,你需要:

  1. 指定编码:fs.readFileSync('test.txt', 'utf8')
  2. 转换 Buffer:content.toString()

在 Node.js 文档中了解更多关于 fs.readFileSync 的信息

哪一组事件通常用于可读流?

可读流会触发几个重要事件:

  • ‘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’ 不存在,则会抛出错误。

这类似于 shell 命令 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. 被传递到标准输出

这创建了一个将所有输入转换为大写的管道。

了解更多关于转换流的信息

当文件被修改时,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. 保存到临时文件
  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');

了解更多关于符号链接的信息

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);
});

文件描述符是操作系统中唯一标识打开文件的数字。

前三个文件描述符是保留的:

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误

始终记得关闭文件描述符:

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

了解更多关于文件描述符的信息

这个字符串在 UTF-8 编码下占用多少字节?

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

在 UTF-8 中:

  • ASCII 字符(如 ‘Hello ‘)每个占 1 字节
  • 地球表情 🌍 占 4 字节

因此:5(Hello)+ 1(空格)+ 4(🌍)= 10 字节

查看字节的方法:

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

了解更多关于 UTF-8 编码

我希望你喜欢测试你的NodeJS IO知识!想要更多?看看我的测验合集获取更多挑战!