Node.js v11.14.0 文档


readline(逐行读取)#

中英对照提交修改

稳定性: 2 - 稳定

readline 模块提供了一个接口,用于一次一行地读取可读流(例如 process.stdin)中的数据。 它可以使用以下方式访问:

const readline = require('readline');

以下的简单示例说明了 readline 模块的基本用法。

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('你如何看待 Node.js 中文网?', (answer) => {
  // TODO:将答案记录在数据库中。
  console.log(`感谢您的宝贵意见:${answer}`);

  rl.close();
});

一旦调用此代码,Node.js 应用程序将不会终止,直到 readline.Interface 关闭,因为接口在 input 流上等待接收数据。

Interface 类#

中英对照提交修改

readline.Interface 类的实例是使用 readline.createInterface() 方法构造的。 每个实例都关联一个 input 可读流和一个 output 可写流output 流用于为到达的用户输入打印提示,并从 input 流读取。

'close' 事件#

中英对照提交修改

当发生以下任一情况时会触发 'close' 事件:

  • 调用 rl.close() 方法,且 readline.Interface 实例放弃对 input 流和 output 流的控制;
  • input 流接收到其 'end' 事件;
  • input 流接收到 <ctrl>-D 以发信号传输结束(EOT);
  • input 流接收到 <ctrl>-C 以发信号 SIGINT,并且 readline.Interface 实例上没有注册 'SIGINT' 事件监听器。

调用监听器函数不传入任何参数。

一旦触发 'close' 事件,则 readline.Interface 实例就完成了。

'line' 事件#

中英对照提交修改

每当 input 流接收到行尾输入(\n\r\r\n)时就会触发 'line' 事件。 这种情况通常发生在当用户按下 <Enter> 键或 <Return> 键。

调用监听器函数时会带上包含接收到的那一行输入的字符串。

rl.on('line', (input) => {
  console.log(`接收到:${input}`);
});

'pause' 事件#

中英对照提交修改

当发生以下任一情况时会触发 'pause' 事件:

  • input 流被暂停。
  • input 流未暂停,且接收到 'SIGCONT' 事件。(参阅 'SIGTSTP' 事件和 'SIGCONT' 事件)

调用监听器函数时不传入任何参数。

rl.on('pause', () => {
  console.log('Readline 暂停');
});

'resume' 事件#

中英对照提交修改

每当 input 流恢复时,就会触发 'resume' 事件。

调用监听器函数时不传入任何参数。

rl.on('resume', () => {
  console.log('Readline 恢复');
});

'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('确定要退出吗?', (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('捕获 SIGTSTP');
});

Windows 上不支持 'SIGTSTP' 事件。

rl.close()#

中英对照提交修改

rl.close() 方法会关闭 readline.Interface 实例,并放弃对 inputoutput 流的控制。 当调用时,将触发 'close' 事件。

调用 rl.close() 不会立即停止 readline.Interface 实例触发的其他事件(包括 'line')。

rl.pause()#

中英对照提交修改

rl.pause() 方法会暂停 input 流,允许稍后在必要时恢复它。

调用 rl.pause() 不会立刻暂停 readline.Interface 实例触发的其他事件(包括 'line')。

rl.prompt([preserveCursor])#

中英对照提交修改

  • preserveCursor <boolean> 如果为 true,则阻止将光标落点重置为 0

rl.prompt() 方法将 readline.Interface 实例配置的提示写入 output 中的新一行,以便为用户提供一个可供输入的新位置。

当调用时,如果 input 流已暂停,则 rl.prompt() 将恢复它。

如果 readline.Interface 创建时 output 被设置为 nullundefined,则不会写入提示。

rl.question(query, callback)#

中英对照提交修改

  • query <string> 要写入 output 的语句或询问,前置于提示符。
  • callback <Function> 回调函数,调用时传入用户的输入以响应 query

rl.question() 方法通过将 query 写入 output 来显示它,并等待用户在 input 上提供输入,然后调用 callback 函数将提供的输入作为第一个参数传入。

当调用时,如果 input 流已暂停,则 rl.question() 将恢复 input 流。

如果 readline.Interface 创建时 output 被设置为 nullundefined,则不会写入 query

用法示例:

rl.question('你最喜欢的食物是什么?', (answer) => {
  console.log(`你最喜欢的食物是 ${answer}`);
});

传给 rl.question()callback 函数不遵循接受 Error 对象或 null 作为第一个参数的经典模式。 调用 callback 时使用提供的答案作为唯一的参数。

rl.resume()#

中英对照提交修改

如果 input 流已暂停,则 rl.resume() 方法将恢复它。

rl.setPrompt(prompt)#

中英对照提交修改

rl.setPrompt() 方法设置每当调用 rl.prompt() 时将写入 output 的提示。

rl.write(data[, key])#

中英对照提交修改

rl.write() 方法将 datakey 标识的按键序列写入 output。 仅当 outputTTY 文本终端时才支持 key 参数。

如果指定了 key,则忽略 data

当调用时,如果 input 流已暂停,则 rl.write() 将恢复它。

如果 readline.Interface 创建时 output 被设置为 nullundefined,则不会写入 datakey

rl.write('删除这个!');
// 模拟 Ctrl+u 删除先前写入的行。
rl.write(null, { ctrl: true, name: 'u' });

rl.write() 方法将数据写入 readlineInterfaceinput,就像它是由用户提供的一样。

rl[Symbol.asyncIterator]()#

暂无中英对照

Stability: 2 - Stable

Create an AsyncIterator object that iterates through each line in the input stream as a string. This method allows asynchronous iteration of readline.Interface objects through for-await-of loops.

Errors in the input stream are not forwarded.

If the loop is terminated with break, throw, or return, rl.close() will be called. In other words, iterating over a readline.Interface will always consume the input stream fully.

A caveat with using this experimental API is that the performance is currently not on par with the traditional 'line' event API, and thus it is not recommended for performance-sensitive applications. We expect this situation to improve in the future.

async function processLineByLine() {
  const rl = readline.createInterface({
    // ...
  });

  for await (const line of rl) {
    // Each line in the readline input will be successively available here as
    // `line`.
  }
}

readline.clearLine(stream, dir)#

中英对照提交修改

readline.clearLine() 方法在由 dir 标识的指定方向上清除给定的 TTY 流的当前行。

readline.clearScreenDown(stream)#

中英对照提交修改

readline.clearScreenDown() 方法从光标的当前位置向下清除给定的 TTY 流。

readline.createInterface(options)#

查看v10.x中文文档

  • options <Object>

    • input <stream.Readable> The Readable stream to listen to. This option is required.
    • output <stream.Writable> The Writable stream to write readline data to.
    • completer <Function> An optional function used for Tab autocompletion.
    • terminal <boolean> true if the input and output streams should be treated like a TTY, and have ANSI/VT100 escape codes written to it. Default: checking isTTY on the output stream upon instantiation.
    • historySize <number> Maximum number of history lines retained. To disable the history set this value to 0. This option makes sense only if terminal is set to true by the user or by an internal output check, otherwise the history caching mechanism is not initialized at all. Default: 30.
    • prompt <string> The prompt string to use. Default: '> '.
    • crlfDelay <number> If the delay between \r and \n exceeds crlfDelay milliseconds, both \r and \n will be treated as separate end-of-line input. crlfDelay will be coerced to a number no less than 100. It can be set to Infinity, in which case \r followed by \n will always be considered a single newline (which may be reasonable for reading files with \r\n line delimiter). Default: 100.
    • removeHistoryDuplicates <boolean> If true, when a new input line added to the history list duplicates an older one, this removes the older line from the list. Default: false.
    • escapeCodeTimeout <number> The duration readline will wait for a character (when reading an ambiguous key sequence in milliseconds one that can both form a complete key sequence using the input read so far and can take additional input to complete a longer key sequence). Default: 500.

The readline.createInterface() method creates a new readline.Interface instance.

const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

Once the readline.Interface instance is created, the most common case is to listen for the 'line' event:

rl.on('line', (line) => {
  console.log(`Received: ${line}`);
});

If terminal is true for this instance then the output stream will get the best compatibility if it defines an output.columns property and emits a 'resize' event on the output if or when the columns ever change (process.stdout does this automatically when it is a TTY).

completer 函数的使用#

查看v10.x中文文档

The completer function takes the current line entered by the user as an argument, and returns an Array with 2 entries:

  • An Array with matching entries for the completion.
  • The substring that was used for the matching.

For instance: [[substr1, substr2, ...], originalsubstring].

function completer(line) {
  const completions = '.help .error .exit .quit .q'.split(' ');
  const hits = completions.filter((c) => c.startsWith(line));
  // Show all completions if none found
  return [hits.length ? hits : completions, line];
}

The completer function can be called asynchronously if it accepts two arguments:

function completer(linePartial, callback) {
  callback(null, [['123'], linePartial]);
}

readline.cursorTo(stream, x, y)#

中英对照提交修改

readline.cursorTo() 方法将光标移动到给定的 TTY stream 中的指定位置。

readline.emitKeypressEvents(stream[, interface])#

中英对照提交修改

readline.emitKeypressEvents() 方法使给定的可读流开始触发与接收的输入相对应的 'keypress' 事件。

可选的 interface 指定 readline.Interface 实例,当检测到复制粘贴输入时,将禁用自动补全。

如果 streamTTY,则它必须处于原始模式。

如果 input 是终端,则由其 input 上的任何 readline 实例自动调用。 关闭 readline 实例不会阻止 input 触发 'keypress' 事件。

readline.emitKeypressEvents(process.stdin);
if (process.stdin.isTTY)
  process.stdin.setRawMode(true);

readline.moveCursor(stream, dx, dy)#

中英对照提交修改

readline.moveCursor() 方法相对于给定的 TTY stream 中的当前位置移动光标。

示例:微型 CLI#

中英对照提交修改

以下示例说明了如何使用 readline.Interface 类来实现一个小型命令行界面:

const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: '请输入> '
});

rl.prompt();

rl.on('line', (line) => {
  switch (line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log(`你输入的是:'${line.trim()}'`);
      break;
  }
  rl.prompt();
}).on('close', () => {
  console.log('再见!');
  process.exit(0);
});

示例:逐行读取文件流#

查看v10.x中文文档

A common use case for readline is to consume an input file one line at a time. The easiest way to do so is leveraging the fs.ReadStream API as well as a for-await-of loop:

const fs = require('fs');
const readline = require('readline');

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();

Alternatively, one could use the 'line' event:

const fs = require('fs');
const readline = require('readline');

const rl = readline.createInterface({
  input: fs.createReadStream('sample.txt'),
  crlfDelay: Infinity
});

rl.on('line', (line) => {
  console.log(`Line from file: ${line}`);
});

Currently, for-await-of loop can be a bit slower. If async / await flow and speed are both essential, a mixed approach can be applied:

const { once } = require('events');
const { createReadStream } = require('fs');
const { createInterface } = require('readline');

(async function processLineByLine() {
  try {
    const rl = createInterface({
      input: createReadStream('big-file.txt'),
      crlfDelay: Infinity
    });

    rl.on('line', (line) => {
      // Process the line.
    });

    await once(rl, 'close');

    console.log('File processed.');
  } catch (err) {
    console.error(err);
  }
})();