readline 逐行读取
node:readline
模块提供了用于从可读流(例如 process.stdin
)每次一行地读取数据的接口。
要使用基于 promise 的 API:
import * as readline from 'node:readline/promises';
const readline = require('node:readline/promises');
要使用回调和同步的 API:
import * as readline from 'node:readline';
const readline = require('node:readline');
下面的简单示例阐明了 node:readline
模块的基本用法。
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const rl = readline.createInterface({ input, output });
const answer = await rl.question('What do you think of Node.js? ');
console.log(`Thank you for your valuable feedback: ${answer}`);
rl.close();
const readline = require('node:readline');
const { stdin: input, stdout: output } = require('node:process');
const rl = readline.createInterface({ input, output });
rl.question('What do you think of Node.js? ', (answer) =\> {
// TODO:记录答案到数据库中
console.log(`Thank you for your valuable feedback: ${answer}`);
rl.close();
});
一旦调用此代码,则 Node.js 应用程序将不会终止,直到 readline.Interface
关闭,因为接口在 input
流上等待接收数据。
InterfaceConstructor
类
- 继承自: <EventEmitter>
InterfaceConstructor
类的实例是使用 readlinePromises.createInterface()
或 readline.createInterface()
方法构造的。 每个实例都与单个 input
可读流和单个 output
可写流相关联。 output
流用于打印到达并从 input
流中读取的用户输入的提示。
'close'
事件
发生以下情况之一时会触发 'close'
事件:
rl.close()
方法被调用,InterfaceConstructor
实例放弃了对input
和output
流的控制;input
流接收到它的'end'
事件;input
流接收 Ctrl+D 以发出传输结束(EOT)的信号;input
流接收 Ctrl+C 以发出SIGINT
信号,并且在InterfaceConstructor
实例上没有注册'SIGINT'
事件监听器。
调用监听器函数时不传入任何参数。
一旦触发 'close'
事件,则 InterfaceConstructor
实例就完成了。
'line'
事件
每当 input
流接收到行尾输入(\n
、\r
或 \r\n
)时,则会触发 'line'
事件。 这通常发生在用户按下 回车 或 返回 时。
如果从流中读取了新数据并且该流在没有最终行尾标记的情况下结束,也会触发 'line'
事件。
使用包含单行接收输入的字符串调用监听器函数。
rl.on('line', (input) =\> {
console.log(`Received: ${input}`);
});
'history'
事件
每当历史数组发生更改时,则会触发 'history'
事件。
使用包含历史数组的数组调用监听器函数。 它将反映由于 historySize
和 removeHistoryDuplicates
引起的所有更改、添加的行和删除的行。
主要目的是允许监听器保留历史记录。 监听器也可以更改历史对象。 这可能有助于防止将某些行添加到历史记录中,例如密码。
rl.on('history', (history) =\> {
console.log(`Received: ${history}`);
});
'pause'
事件
发生以下情况之一时会触发 'pause'
事件:
input
流已暂停。input
流没有暂停并接收'SIGCONT'
事件。 (参见事件 [SIGTSTP
和 [SIGCONT
。)
调用监听器函数时不传入任何参数。
rl.on('pause', () =\> {
console.log('Readline paused.');
});
'resume'
事件
每当 input
流恢复时,则会触发 'resume'
事件。
调用监听器函数时不传入任何参数。
rl.on('resume', () =\> {
console.log('Readline resumed.');
});
'SIGCONT'
事件
当之前使用 Ctrl+Z(即 SIGTSTP
)移动到后台的 Node.js 进程然后使用 fg(1p)
返回到前台时,则会触发 'SIGCONT'
事件。
如果 input
流在 SIGTSTP
请求之前暂停,则不会触发此事件。
调用监听器函数时不传入任何参数。
rl.on('SIGCONT', () =\> {
// `prompt` 会自动恢复流
rl.prompt();
});
Windows 不支持 'SIGCONT'
事件。
'SIGINT'
事件
每当 input
流接收到 Ctrl+C 输入(通常称为 SIGINT
)时,则会触发 'SIGINT'
事件。 如果在 input
流接收到 SIGINT
时没有注册 'SIGINT'
事件监听器,则将触发 'pause'
事件。
调用监听器函数时不传入任何参数。
rl.on('SIGINT', () =\> {
rl.question('Are you sure you want to exit? ', (answer) =\> {
if (answer.match(/^y(es)?$/i)) rl.pause();
});
});
'SIGTSTP'
事件
当 input
流接收到 Ctrl+Z 输入(通常称为 SIGTSTP
)时,则会触发 'SIGTSTP'
事件。 如果 input
流接收到 SIGTSTP
时没有注册 'SIGTSTP'
事件监听器,则 Node.js 进程将被发送到后台。
当使用 fg(1p)
恢复程序时,则将触发 'pause'
和 'SIGCONT'
事件。 这些可用于恢复 input
流。
如果 input
在进程发送到后台之前暂停,则不会触发 'pause'
和 'SIGCONT'
事件。
调用监听器函数时不传入任何参数。
rl.on('SIGTSTP', () =\> {
// 这将覆盖 SIGTSTP
// 并且阻止程序进入后台。
console.log('Caught SIGTSTP.');
});
Windows 不支持 'SIGTSTP'
事件。
rl.close()
rl.close()
方法关闭 InterfaceConstructor
实例并放弃对 input
和 output
流的控制。 当调用时,将触发 'close'
事件。
调用 rl.close()
不会立即阻止其他由 InterfaceConstructor
实例触发的事件(包括 'line'
)。
rl.pause()
rl.pause()
方法暂停 input
流,允许它稍后在必要时恢复。
调用 rl.pause()
不会立即暂停其他由 InterfaceConstructor
实例触发的事件(包括 'line'
)。
rl.prompt([preserveCursor])
preserveCursor
\<boolean\> 如果为true
,则防止光标位置重置为0
。
rl.prompt()
方法将配置为 prompt
的 InterfaceConstructor
实例写入 output
中的新行,以便为用户提供用于提供输入的新位置。
当调用时,如果 rl.prompt()
流已暂停,则 rl.prompt()
将恢复 input
流。
如果 InterfaceConstructor
是在 output
设置为 null
或 undefined
的情况下创建的,则不会写入提示。
rl.question(query[, options], callback)
query
<string> 要写入output
的语句或查询,位于提示之前。options
<Object>signal
<AbortSignal> 可选择允许使用AbortController
取消question()
。
callback
<Function> 使用用户输入调用的回调函数以响应query
。
rl.question()
方法通过将 query
写入 output
来显示 query
,等待在 input
上提供用户输入,然后调用 callback
函数,将提供的输入作为第一个参数传入。
当调用时,如果 rl.question()
流已暂停,则 rl.question()
将恢复 input
流。
如果 InterfaceConstructor
是在 output
设置为 null
或 undefined
的情况下创建的,则不会写入 query
。
传给 rl.question()
的 callback
函数不遵循接受 Error
对象或 null
作为第一个参数的典型模式。 以提供的答案作为唯一参数调用 callback
。
在 rl.close()
之后调用 rl.question()
会报错。
用法示例:
rl.question('What is your favorite food? ', (answer) =\> {
console.log(`Oh, so your favorite food is ${answer}`);
});
使用 AbortController
取消问题。
const ac = new AbortController();
const signal = ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) =\> {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () =\> {
console.log('The food question timed out');
}, { once: true });
setTimeout(() =\> ac.abort(), 10000);
rl.resume()
如果 input
流已暂停,则 rl.resume()
方法会恢复该流。
rl.setPrompt(prompt)
prompt
<string>
rl.setPrompt()
方法设置了在调用 rl.prompt()
时将写入 output
的提示。
rl.getPrompt()
- 返回: <string> 当前的提示字符串
rl.getPrompt()
方法返回 rl.prompt()
使用的当前提示。
rl.write(data[, key])
rl.write()
方法会将 data
或由 key
标识的键序列写入 output
。 仅当 output
是 TTY 文本终端时才支持 key
参数。 有关组合键的列表,请参阅 TTY 快捷键。
如果指定了 key
,则忽略 data
。
当调用时,如果 rl.write()
流已暂停,则 rl.write()
将恢复 input
流。
如果 InterfaceConstructor
是在 output
设置为 null
或 undefined
的情况下创建的,则不会写入 data
和 key
。
rl.write('Delete this!');
// 模拟 Ctrl+U 删除之前写的行
rl.write(null, { ctrl: true, name: 'u' });
rl.write()
方法将数据写入 readline
Interface
的 input
,就好像它是由用户提供的一样。
rl[Symbol.asyncIterator]()
- 返回: <AsyncIterator>
创建 AsyncIterator
对象,该对象遍历输入流中的每一行作为字符串。 此方法允许通过 for await...of
循环异步迭代 InterfaceConstructor
对象。
输入流中的错误不会被转发。
如果循环以 break
、throw
或 return
终止,则将调用 rl.close()
。 换句话说,迭代 InterfaceConstructor
将始终完全消费输入流。
性能无法与传统的 'line'
事件 API 相提并论。 对于性能敏感的应用程序,请改用 'line'
。
async function processLineByLine() {
const rl = readline.createInterface({
// ...
});
for await (const line of rl) {
// 逐行读取输入中的每一行
// 都将在此处作为 `line` 连续可用。
}
}
readline.createInterface()
将在调用后开始使用输入流。 在接口创建和异步迭代之间进行异步操作可能会导致丢失行。
rl.line
节点正在处理的当前输入数据。
这可用于从 TTY 流中收集输入以检索迄今为止(在 line
事件触发之前)已处理的当前值。 一旦触发 line
事件,则此属性将是空字符串。
请注意,如果 rl.cursor
也不受控制,则在实例运行时修改该值可能会产生意想不到的后果。
如果不使用 TTY 流进行输入,则使用 'line'
事件。
一个可能的用例如下:
const values = ['lorem ipsum', 'dolor sit amet'];
const rl = readline.createInterface(process.stdin);
const showResults = debounce(() =\> {
console.log(
'\n',
values.filter((val) =\> val.startsWith(rl.line)).join(' ')
);
}, 300);
process.stdin.on('keypress', (c, k) =\> {
showResults();
});
rl.cursor
相对于 rl.line
的光标位置。
当从 TTY 流读取输入时,这将跟踪当前光标在输入字符串中的位置。 光标的位置决定了在处理输入时将被修改的输入字符串部分,以及将呈现终端插入符号的列。
rl.getCursorPos()
返回光标相对于输入提示 + 字符串的实际位置。 长输入(换行)字符串以及多行提示都包含在计算中。
Promises API
readlinePromises.Interface
类
readlinePromises.Interface
类的实例是使用 readlinePromises.createInterface()
方法构造的。 每个实例都与单个 input
可读流和单个 output
可写流相关联。 output
流用于打印到达并从 input
流中读取的用户输入的提示。
rl.question(query[, options])
query
<string> 要写入output
的语句或查询,位于提示之前。options
<Object>signal
<AbortSignal> 可选择允许使用AbortSignal
取消question()
。
- 返回: <Promise> 使用用户响应
query
的输入履行的 promise。
rl.question()
方法通过将 query
写入 output
来显示 query
,等待在 input
上提供用户输入,然后调用 callback
函数,将提供的输入作为第一个参数传入。
当调用时,如果 rl.question()
流已暂停,则 rl.question()
将恢复 input
流。
如果 readlinePromises.Interface
是在 output
设置为 null
或 undefined
的情况下创建的,则不会写入 query
。
如果问题在 rl.close()
之后被调用,则它返回被拒绝的 promise。
用法示例:
const answer = await rl.question('What is your favorite food? ');
console.log(`Oh, so your favorite food is ${answer}`);
使用 AbortSignal
取消问题。
const signal = AbortSignal.timeout(10_000);
signal.addEventListener('abort', () =\> {
console.log('The food question timed out');
}, { once: true });
const answer = await rl.question('What is your favorite food? ', { signal });
console.log(`Oh, so your favorite food is ${answer}`);
readlinePromises.Readline
类
new readlinePromises.Readline(stream[, options])
stream
<stream.Writable> TTY 流。options
<Object>autoCommit
<boolean> 如果是true
,则不需要调用rl.commit()
。
rl.clearLine(dir)
dir
<integer>-1
: 从光标向左1
: 从光标向右0
: 整行
- 返回: this
rl.clearLine()
方法在待处理动作的内部列表中添加一个动作,该动作在 dir
标识的指定方向上清除关联 stream
的当前行。 调用 rl.commit()
看看这个方法的效果,除非 autoCommit: true
传给了构造函数。
rl.clearScreenDown()
- 返回: this
rl.clearScreenDown()
方法向待处理动作的内部列表添加一个动作,该动作从光标向下的当前位置清除关联流。 调用 rl.commit()
看看这个方法的效果,除非 autoCommit: true
传给了构造函数。
rl.commit()
- 返回: <Promise>
rl.commit()
方法将所有待处理的操作发送到关联的 stream
并清除待处理操作的内部列表。
rl.cursorTo(x[, y])
rl.cursorTo()
方法向待处理动作的内部列表添加一个动作,该动作将光标移动到相关 stream
中指定的位置。 调用 rl.commit()
看看这个方法的效果,除非 autoCommit: true
传给了构造函数。
rl.moveCursor(dx, dy)
rl.moveCursor()
方法向待处理动作的内部列表添加一个动作,该动作相对于其在关联 stream
中的当前位置移动光标。 调用 rl.commit()
看看这个方法的效果,除非 autoCommit: true
传给了构造函数。
rl.rollback()
- 返回: this
rl.rollback
方法清除内部待处理操作列表,而不将其发送到关联的 stream
。
readlinePromises.createInterface(options)
options
<Object>input
<stream.Readable> 要监听的可读流。 此选项是必需的。output
<stream.Writable> 要将逐行读取的数据写入的可写流。completer
<Function> 可选的用于制表符自动补全的函数。terminal
<boolean> 如果input
和output
流应该被视为终端,并且写入了 ANSI/VT100 转义码,则为true
。 默认值: 在实例化时检查output
流上的isTTY
。history
<string[]> 历史行的初始列表。 仅当terminal
由用户或内部的output
检查设置为true
时,此选项才有意义,否则历史缓存机制根本不会初始化。 默认值:[]
。historySize
<number> 保留的最大历史行数。 要禁用历史记录,则将此值设置为0
。 仅当terminal
由用户或内部的output
检查设置为true
时,此选项才有意义,否则历史缓存机制根本不会初始化。 默认值:30
。removeHistoryDuplicates
<boolean> 如果为true
,则当添加到历史列表的新输入行与旧输入行重复时,这将从列表中删除旧行。 默认值:false
。prompt
<string> 要使用的提示字符串。 默认值:'\> '
。crlfDelay
<number> 如果\r
和\n
之间的延迟超过crlfDelay
毫秒,则\r
和\n
都将被视为单独的行尾输入。crlfDelay
将被强制为不小于100
的数字。 它可以设置为Infinity
,在这种情况下,\r
后跟\n
将始终被视为单个换行符(这对于具有\r\n
行分隔符的文件读取可能是合理的)。 默认值:100
。escapeCodeTimeout
<number>readlinePromises
将等待字符的时长(当以毫秒为单位读取不明确的键序列时,既可以使用目前读取的输入形成完整的键序列,又可以采用额外的输入来完成更长的键序列)。 默认值:500
。tabSize
<integer> 一个制表符等于的空格数(最小为 1)。 默认值:8
。
- 返回: <readlinePromises.Interface>
readlinePromises.createInterface()
方法创建新的 readlinePromises.Interface
实例。
const readlinePromises = require('node:readline/promises');
const rl = readlinePromises.createInterface({
input: process.stdin,
output: process.stdout
});
一旦创建了 readlinePromises.Interface
实例,则最常见的场景就是监听 'line'
事件:
rl.on('line', (line) =\> {
console.log(`Received: ${line}`);
});
如果此实例的 terminal
是 true
,则如果它定义了 output.columns
属性,并且如果或当列发生变化时(process.stdout
会当其是终端时自动执行此操作)在 output
上触发 'resize'
事件,则 output
流将获得最佳的兼容性。
completer 函数的使用
completer
函数将用户输入的当前行作为参数,并返回包含 2 个条目的 Array
:
- 使用匹配条目的
Array
补全。 - 用于匹配的子字符串。
例如:[[substr1, substr2, ...], originalsubstring]
。
function completer(line) {
const completions = '.help .error .exit .quit .q'.split(' ');
const hits = completions.filter((c) =\> c.startsWith(line));
// 如果没有找到,则显示所有补全
return [hits.length ? hits : completions, line];
}
completer
函数也可以返回 <Promise>,或者是异步的:
async function completer(linePartial) {
await someAsyncWork();
return [['123'], linePartial];
}
Callback API
readline.Interface
类
readline.Interface
类的实例是使用 readline.createInterface()
方法构造的。 每个实例都与单个 input
可读流和单个 output
可写流相关联。 output
流用于打印到达并从 input
流中读取的用户输入的提示。
rl.question(query[, options], callback)
query
<string> 要写入output
的语句或查询,位于提示之前。options
<Object>signal
<AbortSignal> 可选择允许使用AbortController
取消question()
。
callback
<Function> 使用用户输入调用的回调函数以响应query
。
rl.question()
方法通过将 query
写入 output
来显示 query
,等待在 input
上提供用户输入,然后调用 callback
函数,将提供的输入作为第一个参数传入。
当调用时,如果 rl.question()
流已暂停,则 rl.question()
将恢复 input
流。
如果 readline.Interface
是在 output
设置为 null
或 undefined
的情况下创建的,则不会写入 query
。
传给 rl.question()
的 callback
函数不遵循接受 Error
对象或 null
作为第一个参数的典型模式。 以提供的答案作为唯一参数调用 callback
。
在 rl.close()
之后调用 rl.question()
会报错。
用法示例:
rl.question('What is your favorite food? ', (answer) =\> {
console.log(`Oh, so your favorite food is ${answer}`);
});
使用 AbortController
取消问题。
const ac = new AbortController();
const signal = ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) =\> {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () =\> {
console.log('The food question timed out');
}, { once: true });
setTimeout(() =\> ac.abort(), 10000);
readline.clearLine(stream, dir[, callback])
stream
<stream.Writable>dir
<number>-1
: 从光标向左1
: 从光标向右0
: 整行
callback
<Function> 操作完成后调用。- 返回: <boolean> 如果
stream
希望调用代码在继续写入额外的数据之前等待'drain'
事件被触发,则为false
;否则为true
。
readline.clearLine()
方法在 dir
标识的指定方向上清除给定 TTY 流的当前行。
readline.clearScreenDown(stream[, callback])
stream
<stream.Writable>callback
<Function> 操作完成后调用。- 返回: <boolean> 如果
stream
希望调用代码在继续写入额外的数据之前等待'drain'
事件被触发,则为false
;否则为true
。
readline.clearScreenDown()
方法从光标的当前位置向下清除给定的 TTY 流。
readline.createInterface(options)
options
<Object>input
<stream.Readable> 要监听的可读流。 此选项是必需的。output
<stream.Writable> 要将逐行读取的数据写入的可写流。completer
<Function> 可选的用于制表符自动补全的函数。terminal
<boolean> 如果input
和output
流应该被视为终端,并且写入了 ANSI/VT100 转义码,则为true
。 默认值: 在实例化时检查output
流上的isTTY
。history
<string[]> 历史行的初始列表。 仅当terminal
由用户或内部的output
检查设置为true
时,此选项才有意义,否则历史缓存机制根本不会初始化。 默认值:[]
。historySize
<number> 保留的最大历史行数。 要禁用历史记录,则将此值设置为0
。 仅当terminal
由用户或内部的output
检查设置为true
时,此选项才有意义,否则历史缓存机制根本不会初始化。 默认值:30
。removeHistoryDuplicates
<boolean> 如果为true
,则当添加到历史列表的新输入行与旧输入行重复时,这将从列表中删除旧行。 默认值:false
。prompt
<string> 要使用的提示字符串。 默认值:'\> '
。crlfDelay
<number> 如果\r
和\n
之间的延迟超过crlfDelay
毫秒,则\r
和\n
都将被视为单独的行尾输入。crlfDelay
将被强制为不小于100
的数字。 它可以设置为Infinity
,在这种情况下,\r
后跟\n
将始终被视为单个换行符(这对于具有\r\n
行分隔符的文件读取可能是合理的)。 默认值:100
。escapeCodeTimeout
<number>readline
将等待字符的时长(当以毫秒为单位读取不明确的键序列时,既可以使用目前读取的输入形成完整的键序列,又可以采用额外的输入来完成更长的键序列)。 默认值:500
。tabSize
<integer> 一个制表符等于的空格数(最小为 1)。 默认值:8
。signal
<AbortSignal> 允许使用中止信号关闭接口。 中止信号将在内部调用接口上的close
。
- 返回: <readline.Interface>
readline.createInterface()
方法创建新的 readline.Interface
实例。
const readline = require('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
一旦创建了 readline.Interface
实例,则最常见的场景就是监听 'line'
事件:
rl.on('line', (line) =\> {
console.log(`Received: ${line}`);
});
如果此实例的 terminal
是 true
,则如果它定义了 output.columns
属性,并且如果或当列发生变化时(process.stdout
会当其是终端时自动执行此操作)在 output
上触发 'resize'
事件,则 output
流将获得最佳的兼容性。
当使用 stdin
作为输入创建 readline.Interface
时,则程序在收到 EOF 字符之前不会终止。 要在不等待用户输入的情况下退出,则调用 process.stdin.unref()
。
completer 函数的使用
completer
函数将用户输入的当前行作为参数,并返回包含 2 个条目的 Array
:
- 使用匹配条目的
Array
补全。 - 用于匹配的子字符串。
例如:[[substr1, substr2, ...], originalsubstring]
。
function completer(line) {
const completions = '.help .error .exit .quit .q'.split(' ');
const hits = completions.filter((c) =\> c.startsWith(line));
// 如果没有找到,则显示所有补全
return [hits.length ? hits : completions, line];
}
如果 completer
函数接受两个参数,则可以异步地调用它:
function completer(linePartial, callback) {
callback(null, [['123'], linePartial]);
}
readline.cursorTo(stream, x[, y][, callback])
stream
<stream.Writable>x
<number>y
<number>callback
<Function> 操作完成后调用。- 返回: <boolean> 如果
stream
希望调用代码在继续写入额外的数据之前等待'drain'
事件被触发,则为false
;否则为true
。
readline.cursorTo()
方法将光标移动到给定的 TTY stream
中的指定位置。
readline.moveCursor(stream, dx, dy[, callback])
stream
<stream.Writable>dx
<number>dy
<number>callback
<Function> 操作完成后调用。- 返回: <boolean> 如果
stream
希望调用代码在继续写入额外的数据之前等待'drain'
事件被触发,则为false
;否则为true
。
readline.moveCursor()
方法相对于它在给定的 TTY stream
中的当前位置移动光标。
readline.emitKeypressEvents(stream[, interface])
stream
<stream.Readable>interface
<readline.InterfaceConstructor>
readline.emitKeypressEvents()
方法使给定的可读流开始触发与接收到的输入相对应的 'keypress'
事件。
可选地,interface
指定 readline.Interface
实例,当检测到复制粘贴输入时禁用自动完成。
如果 stream
是 TTY,则它必须处于原始模式。
如果 input
是终端,则它会被其 input
上的任何逐行读取实例自动调用。 关闭 readline
实例不会阻止 input
触发 'keypress'
事件。
readline.emitKeypressEvents(process.stdin);
if (process.stdin.isTTY)
process.stdin.setRawMode(true);
示例
微型 CLI
下面的例子说明了使用 readline.Interface
类来实现一个微型的命令行界面:
const readline = require('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'OHAI\> '
});
rl.prompt();
rl.on('line', (line) =\> {
switch (line.trim()) {
case 'hello':
console.log('world!');
break;
default:
console.log(`Say what? I might have heard '${line.trim()}'`);
break;
}
rl.prompt();
}).on('close', () =\> {
console.log('Have a great day!');
process.exit(0);
});
逐行读取文件流
readline
的一个常见用例是每次一行地消费输入文件。 最简单的方式是利用 fs.ReadStream
API 和 for await...of
循环:
const fs = require('node:fs');
const readline = require('node:readline');
async function processLineByLine() {
const fileStream = fs.createReadStream('input.txt');
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
// 注意:使用 crlfDelay 选项
// 将 input.txt 中的所有 CR LF ('\r\n') 实例识别为单个换行符。
for await (const line of rl) {
// input.txt 中的每一行都将在此处作为 `line` 连续可用。
console.log(`Line from file: ${line}`);
}
}
processLineByLine();
或者,可以使用 'line'
事件:
const fs = require('node:fs');
const readline = require('node:readline');
const rl = readline.createInterface({
input: fs.createReadStream('sample.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) =\> {
console.log(`Line from file: ${line}`);
});
目前,for await...of
循环可能会慢一点。 如果 async
/ await
流量和速度都必不可少,则可以应用混合方法:
const { once } = require('node:events');
const { createReadStream } = require('node:fs');
const { createInterface } = require('node:readline');
(async function processLineByLine() {
try {
const rl = createInterface({
input: createReadStream('big-file.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) =\> {
// 处理行。
});
await once(rl, 'close');
console.log('File processed.');
} catch (err) {
console.error(err);
}
})();