Node.js中文网的 v6.10.3 文档提供了readline模块,可以从可读流(process.stdin)读取数据,Node.js的v6.10.3 版本是长期支持版本,而readline也是比较稳定的API了,下面详细描述一下readline的官方API;之前写了一个从酷狗音乐爬取音乐歌曲的爬虫脚本,但是每次都要从酷狗官网查找歌手的详细地址,现在可以直接可以使用readline模块从命令行窗口读取我们要查找的歌手名称,然后进行拼接处理。
1. <code>require(‘readline’)</code>模块提供了一个接口,用于从可读流中(process.stdin)中读取数据,每次读取一行,由于readline不是Node.js的全局模块变量,所以需要引入readline模块。
require ('readline);
- 下面是官方给出的readline基本用法:
const readline=require('readline');
const rl=readline.createInterface({
input:process.stdin,
output:process.stdout
});
rl.question('你觉得Node.js中文网怎么样?',(answer)=>{
//对答案进行处理
console.log(`多谢你的反馈:${answer}`);
rl.close();
});
- 运行上述代码并输入你想输入的评价,就可以直接可以输出具体的结果,详细如下:
- 注意,当调用代码时候,Node.js不会终止,只有realine.createInterface被关闭;因为接口在等待 input 流中要被接收的数据。
2. Interface类:readline.Interface类的实例都是使用readline.createInterface()方法构造的。每个实例都关联着一个input可读流和一个output可写流。output流用于用户输入打印显示,output流数据从input流中读取。
- ‘close’事件:当以下之一发生时,触发'close'事件:
- rl.close()方法被调用,且readline.Interface实例已经撤回对input流和output流的控制。
- input流收到end事件。
- input流收到表示结束传输的<ctrl+D>。
- input流收到SIGINT的<ctrl+C>,且readline.interface实例上没有注册SIGINT事件监听器。
- 监听器函数不接受任何参数,当'close'事件被触发时,readline.Interface实例应当被视为结束。
- ‘line’事件:每当input流收到行结束符(\n ,\r或者\n\r时触发‘line事件。通常发生在用户按下<enter>或<return>键)
- 监听器函数被调用时会带上一个包含接收哪一行输入的字符,实例如下包括运行结果。
const readline=require('readline');
const rl=readline.createInterface({
input:process.stdin,
output:process.stdout
});
rl.on('line',(input)=>{
console.log(`接收到: ${input}`);
rl.close();
});
- 'pause'事件:当以下之一事件发生时触发'pause'事件:
- input 流暂停。
- input 流不是暂停的,且收到SIGONT事件。
- 监听函数被调用时不传入任何参数。
- 例子:
rl.on('pause',()=>{
console,log('Readline 被暂停。');
});
- ‘resume’事件:每当input流被恢复时触发'resume'事件。监听器函数被调用时不传入任何参数。
- 实例如下:
rl.on('resume',()=>{
console.log('Readline 被恢复。');
});
- ‘SIGCONT’事件:
- 当一个Node.js进程使用<ctrl +z>(也就是SIGSTP)移入后台之后再使用fg(1)移回前台时,触发'SIGCONT'事件。
- 如果input流在SIGSTP请求之前被暂停,则事件不会被触发。
- 监听器函数被调用时不传任何参数。
- 例子(注意,Windows系统不支持‘SIGCONF’事件):
rl.on('SIGCONF',()=>{
//'prompt'会自动恢复流
rl.prompt();
});
- ‘SIGINT’事件:每当input流接收到一个<ctrl + C>输入(通常被称为SIGINT)时,触发‘SIGINT’事件,当input流接收到一个SIGINT时,如果没有注册‘SIGINT’事件监听器,则‘pause’事件会被触发。
- 监听器函数被调用时不传入任何参数,例子如下。
rl.on('SIGCONF',()=>{
rl.question('确定要退出吗?',(answer)=>{
if(answer.match(/^y(es)?$/i))
rl.pause();
});
});
- 'SIGSTP'事件:每当input流接收到一个<ctrl+z>输入(通常被称为SIGSTP)时,触发‘SIGSTP’事件。当input流接收到一个SIGSTP时,如果没有注册‘SIGSTP’事件监听器,则Node.js进程会被发送到后台。
- 当程序使用fg(1)恢复时,‘pause’和‘SIGCONF’事件会被触发。这可被用来恢复input流。
- 如果input流在进程被发送到后台之前被暂停,则‘pause’和‘SIGCONF’事件不会被触发。
- 实例如下所示(注意,Windows系统不支持‘SIGSTP’):
rl.on('SIGCONF',()=>{
//这会重写SOGSTP,且防止程序进入后台
console.log('捕获SIGSTP');
});
rl.close():rl.close()方法会关闭readline.Interface实例,且撤回对input和output流的控制。但调用时,close事件会被触发。
rl.pause():rl.pause()方法会暂停input流,且稍后需要时可被恢复。调用rl.pause()不会立刻暂停其他事件(包括‘line’)被readline.Interface实例触发。
-
rl.prompt([preserveCursor]):preserveCursor(boolean)如果为true,则阻止光标落点被设置为0。
- rl.prompt()方法会在output流中新的一行写入readlin.Interface实例配置后的prompt,用于为用户提供一个可供输入新的位置。
- 当被调用时,如果input流已经被暂停,则rl.prompt()会回复input流。
- 如果readline.Interface被创建时ouput被设为null或undefined,则提示不会被写入。
-
rl.question(query,callback):
- query(String)一个在提示符之前、要写入output的叙述或提问。
- callback(Function)一个回调函数,它会被调用并带上用户响应query的输入。
- rl.question()方法通过写入output来展示query,并等待用户提供到input的输入,然后调用callback函数并传入提供的输入作为第一个参数。
- 当被调用,如果input流已被暂停,则rl.question()会恢复input流。
- 如果readline.Interface被创建时,output被设为null或undefined,则query不会被写入。
- 实例如下所示(注意,传入的rl.question()的callback函数不支持遵循一个Error对象会Null作为第一个参数的标准模式。callback被调用时只带上提供的答案作为唯一参数):
rl.question('你最喜欢的食物是什么?',(answer)=>{
console.log(`你最喜欢的食物是 ${answer}`);
});
rl.resume():如果input流已被暂停,则rl.resume()方法会恢复input流。
-
rl.setPrompt(prompt):
- prompt(String):rl.setPrompt()方法用于设置每当rl.prompt()被调用时要被写入output的提示。
-
rl.write(data[,key]):
data(String):
-
key(Object):
- ctrl (boolean)如果为true则表示<ctrl>键
- meta (boolean)如果为true则表示<Meat>键
- shift (boolean)如果为true则表示<shift>键
- name (String)一个按键的名称
rl.write()方法会把data或一个由Key指定的按键序列写到output。只有当output是一个TTY文本终端时,Key参数才被支持。
如果指定了key,则data会被忽略。当被调用时,如果input流已经暂停,则rl.write()会恢复input流。
如果readline.Interface被创建时output被设置为null或undefined,则data和key不会写入。
例子如下(注意,rl.write()方法会写入数据到readline接口的input,仿佛他是用户提供的):
rl.write('删除这个!');
//模拟ctrl+u删除写入的前一行
rl.write(null,{
ctrl:true,
name:'u'
});
- readline.clearLine(stream,dir):
- stream(Writable):
- dir(number):
- -1 光标左边
- 1-光标右边
- 0 -整行
- readline.clearLine()方法会以dir指定的方向清除给定的TTY流当前行。
- readline.clearScreenDown(Strem):
- stream(writable)
- readline.clearScreenDown()方法会从光标的当前位置向下清除给定TTY流。
- readline.createInterface(options):
- options:(object)
- input(Readable)要监听的可读流。该选项是必须的。
- output(Writable)要写入逐行读取数据的可写流。
- completer(Function):一个可选的函数,用于Tab自动补全。
- Terminal(boolean):如果input和output应被当做一个tty,且要写入ANSI、VT100转换的代码,则设置为true;默认实例化时在output流上检查isTTY。
- historySize(number):保留的历时行数的最大数量,设为0课禁用历史记录。默认为30.该选项只有terminal被用户或内部output设为true时才有意义,否则历史缓存机制不会被初始化。
- prompt -要使用的提示字符串。默认为'>'.
- crlfDelay(number):如果\r与\n之间的延迟超过crlfDelay毫秒,则\r与\n都会当做换行分隔符。默认为100毫秒。crlfDelay的范围为[100,2000].
- readline.createInterface()方法创建一个新的readline.Intreface实例,例子如下:
- options:(object)
const readline=require('readline');
const rl=readline.createInterface({
input:process.stdin,
output:process.stdout
});
+ 一旦readline.Interface实例被创建,最常见的就是监听‘line’事件:
rl.on('line',(line)=>{
console.log(`接收到:${line}`);
});
+ 如果该实例的terminal为true,则若它定义了一个output.columns属性则output流会获得最佳兼容性,且如果或当列发生变化时,output上触发一个'resize'事件(当它为一个TTY时,process.stdout会自动处理这个)。
+ completer函数使用:当被调用时,用户输入的当前行会被提供给completer函数,并返回一个包含以下两个条目的数组:
+ 一个包含自动补全输入的数组。
+ 用于匹配的字符串。
+ 例如:[[substr1,substr2,...],originalsubstring]。
function completer(line){
const completions='.help .error .quit .q'.split(' ');
const hits=completions.filter((c)=>{return c.indexOf(line)==0});
//如果没匹配到则展示到=全部补全
return [hits.length?hits:completions,line];
}
+ 如果completer函数结束两个参数,则可被异步地调用。
function completer(linePartial,callback){
callback(null,[['123'],linePartial]);
}
- readline.curorTo(stream,x,y):
- stream(writable):
- x (number):
- y(number):
- readline.cursorTo()方法会移动光标到指定的TTY stream中指定的位置。
- readline.emitKeypressEvent(stream[,interface):
- stream(Readable):
- interface(readline.Interface):
- readline.emitKeypressEvents()方法是给定的可写流stream相应接收到的输入触发‘keypress’事件。
- 可选的interface指定了一个readline.Interface实例,用于当自动补全被禁用时检测到复制黏贴输入。
- 如果stream是一个TTY,则它必须为原始模式。
readline.emitKeypressEvents(process.stdin);
if(process.stdin.isTTY)
process.stdin.setRawMode(true);
- readline.moveCursor(stream,dx,xy):
- stream:(writable):
- dx:(number):
- dy(Number):
- readline.moveCursor()方法移动光标到给定的TTY stream中相对当前的位置。
3.例子:简单的命令行界面
- 例子,使用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);
});
4.逐行读取文件流:
- 例子,从一个文件系统可读流中每次一行地消耗输入:
const readline=require('readline');
const fs=require('fs');
const rl=readline.createInterface({
input:fs.createReadStream('sample.txt');
});
rl.on('line',(line)=>{
console.log('文件的单行内容:${line}');
});