Node.js中文網(wǎng)的 v6.10.3 文檔提供了readline模塊,可以從可讀流(process.stdin)讀取數(shù)據(jù),Node.js的v6.10.3 版本是長期支持版本,而readline也是比較穩(wěn)定的API了,下面詳細描述一下readline的官方API;之前寫了一個從酷狗音樂爬取音樂歌曲的爬蟲腳本,但是每次都要從酷狗官網(wǎng)查找歌手的詳細地址,現(xiàn)在可以直接可以使用readline模塊從命令行窗口讀取我們要查找的歌手名稱,然后進行拼接處理。
1. <code>require(‘readline’)</code>模塊提供了一個接口,用于從可讀流中(process.stdin)中讀取數(shù)據(jù),每次讀取一行,由于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中文網(wǎng)怎么樣?',(answer)=>{
//對答案進行處理
console.log(`多謝你的反饋:${answer}`);
rl.close();
});
- 運行上述代碼并輸入你想輸入的評價,就可以直接可以輸出具體的結(jié)果,詳細如下:

- 注意,當(dāng)調(diào)用代碼時候,Node.js不會終止,只有realine.createInterface被關(guān)閉;因為接口在等待 input 流中要被接收的數(shù)據(jù)。
2. Interface類:readline.Interface類的實例都是使用readline.createInterface()方法構(gòu)造的。每個實例都關(guān)聯(lián)著一個input可讀流和一個output可寫流。output流用于用戶輸入打印顯示,output流數(shù)據(jù)從input流中讀取。
- ‘close’事件:當(dāng)以下之一發(fā)生時,觸發(fā)'close'事件:
- rl.close()方法被調(diào)用,且readline.Interface實例已經(jīng)撤回對input流和output流的控制。
- input流收到end事件。
- input流收到表示結(jié)束傳輸?shù)?lt;ctrl+D>。
- input流收到SIGINT的<ctrl+C>,且readline.interface實例上沒有注冊SIGINT事件監(jiān)聽器。
- 監(jiān)聽器函數(shù)不接受任何參數(shù),當(dāng)'close'事件被觸發(fā)時,readline.Interface實例應(yīng)當(dāng)被視為結(jié)束。
- ‘line’事件:每當(dāng)input流收到行結(jié)束符(\n ,\r或者\n\r時觸發(fā)‘line事件。通常發(fā)生在用戶按下<enter>或<return>鍵)
- 監(jiān)聽器函數(shù)被調(diào)用時會帶上一個包含接收哪一行輸入的字符,實例如下包括運行結(jié)果。
const readline=require('readline');
const rl=readline.createInterface({
input:process.stdin,
output:process.stdout
});
rl.on('line',(input)=>{
console.log(`接收到: ${input}`);
rl.close();
});

- 'pause'事件:當(dāng)以下之一事件發(fā)生時觸發(fā)'pause'事件:
- input 流暫停。
- input 流不是暫停的,且收到SIGONT事件。
- 監(jiān)聽函數(shù)被調(diào)用時不傳入任何參數(shù)。
- 例子:
rl.on('pause',()=>{
console,log('Readline 被暫停。');
});
- ‘resume’事件:每當(dāng)input流被恢復(fù)時觸發(fā)'resume'事件。監(jiān)聽器函數(shù)被調(diào)用時不傳入任何參數(shù)。
- 實例如下:
rl.on('resume',()=>{
console.log('Readline 被恢復(fù)。');
});
- ‘SIGCONT’事件:
- 當(dāng)一個Node.js進程使用<ctrl +z>(也就是SIGSTP)移入后臺之后再使用fg(1)移回前臺時,觸發(fā)'SIGCONT'事件。
- 如果input流在SIGSTP請求之前被暫停,則事件不會被觸發(fā)。
- 監(jiān)聽器函數(shù)被調(diào)用時不傳任何參數(shù)。
- 例子(注意,Windows系統(tǒng)不支持‘SIGCONF’事件):
rl.on('SIGCONF',()=>{
//'prompt'會自動恢復(fù)流
rl.prompt();
});
- ‘SIGINT’事件:每當(dāng)input流接收到一個<ctrl + C>輸入(通常被稱為SIGINT)時,觸發(fā)‘SIGINT’事件,當(dāng)input流接收到一個SIGINT時,如果沒有注冊‘SIGINT’事件監(jiān)聽器,則‘pause’事件會被觸發(fā)。
- 監(jiān)聽器函數(shù)被調(diào)用時不傳入任何參數(shù),例子如下。
rl.on('SIGCONF',()=>{
rl.question('確定要退出嗎?',(answer)=>{
if(answer.match(/^y(es)?$/i))
rl.pause();
});
});
- 'SIGSTP'事件:每當(dāng)input流接收到一個<ctrl+z>輸入(通常被稱為SIGSTP)時,觸發(fā)‘SIGSTP’事件。當(dāng)input流接收到一個SIGSTP時,如果沒有注冊‘SIGSTP’事件監(jiān)聽器,則Node.js進程會被發(fā)送到后臺。
- 當(dāng)程序使用fg(1)恢復(fù)時,‘pause’和‘SIGCONF’事件會被觸發(fā)。這可被用來恢復(fù)input流。
- 如果input流在進程被發(fā)送到后臺之前被暫停,則‘pause’和‘SIGCONF’事件不會被觸發(fā)。
- 實例如下所示(注意,Windows系統(tǒng)不支持‘SIGSTP’):
rl.on('SIGCONF',()=>{
//這會重寫SOGSTP,且防止程序進入后臺
console.log('捕獲SIGSTP');
});
rl.close():rl.close()方法會關(guān)閉readline.Interface實例,且撤回對input和output流的控制。但調(diào)用時,close事件會被觸發(fā)。
rl.pause():rl.pause()方法會暫停input流,且稍后需要時可被恢復(fù)。調(diào)用rl.pause()不會立刻暫停其他事件(包括‘line’)被readline.Interface實例觸發(fā)。
-
rl.prompt([preserveCursor]):preserveCursor(boolean)如果為true,則阻止光標(biāo)落點被設(shè)置為0。
- rl.prompt()方法會在output流中新的一行寫入readlin.Interface實例配置后的prompt,用于為用戶提供一個可供輸入新的位置。
- 當(dāng)被調(diào)用時,如果input流已經(jīng)被暫停,則rl.prompt()會回復(fù)input流。
- 如果readline.Interface被創(chuàng)建時ouput被設(shè)為null或undefined,則提示不會被寫入。
-
rl.question(query,callback):
- query(String)一個在提示符之前、要寫入output的敘述或提問。
- callback(Function)一個回調(diào)函數(shù),它會被調(diào)用并帶上用戶響應(yīng)query的輸入。
- rl.question()方法通過寫入output來展示query,并等待用戶提供到input的輸入,然后調(diào)用callback函數(shù)并傳入提供的輸入作為第一個參數(shù)。
- 當(dāng)被調(diào)用,如果input流已被暫停,則rl.question()會恢復(fù)input流。
- 如果readline.Interface被創(chuàng)建時,output被設(shè)為null或undefined,則query不會被寫入。
- 實例如下所示(注意,傳入的rl.question()的callback函數(shù)不支持遵循一個Error對象會Null作為第一個參數(shù)的標(biāo)準(zhǔn)模式。callback被調(diào)用時只帶上提供的答案作為唯一參數(shù)):
rl.question('你最喜歡的食物是什么?',(answer)=>{
console.log(`你最喜歡的食物是 ${answer}`);
});
rl.resume():如果input流已被暫停,則rl.resume()方法會恢復(fù)input流。
-
rl.setPrompt(prompt):
- prompt(String):rl.setPrompt()方法用于設(shè)置每當(dāng)rl.prompt()被調(diào)用時要被寫入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。只有當(dāng)output是一個TTY文本終端時,Key參數(shù)才被支持。
如果指定了key,則data會被忽略。當(dāng)被調(diào)用時,如果input流已經(jīng)暫停,則rl.write()會恢復(fù)input流。
如果readline.Interface被創(chuàng)建時output被設(shè)置為null或undefined,則data和key不會寫入。
例子如下(注意,rl.write()方法會寫入數(shù)據(jù)到readline接口的input,仿佛他是用戶提供的):
rl.write('刪除這個!');
//模擬ctrl+u刪除寫入的前一行
rl.write(null,{
ctrl:true,
name:'u'
});
- readline.clearLine(stream,dir):
- stream(Writable):
- dir(number):
- -1 光標(biāo)左邊
- 1-光標(biāo)右邊
- 0 -整行
- readline.clearLine()方法會以dir指定的方向清除給定的TTY流當(dāng)前行。
- readline.clearScreenDown(Strem):
- stream(writable)
- readline.clearScreenDown()方法會從光標(biāo)的當(dāng)前位置向下清除給定TTY流。
- readline.createInterface(options):
- options:(object)
- input(Readable)要監(jiān)聽的可讀流。該選項是必須的。
- output(Writable)要寫入逐行讀取數(shù)據(jù)的可寫流。
- completer(Function):一個可選的函數(shù),用于Tab自動補全。
- Terminal(boolean):如果input和output應(yīng)被當(dāng)做一個tty,且要寫入ANSI、VT100轉(zhuǎn)換的代碼,則設(shè)置為true;默認實例化時在output流上檢查isTTY。
- historySize(number):保留的歷時行數(shù)的最大數(shù)量,設(shè)為0課禁用歷史記錄。默認為30.該選項只有terminal被用戶或內(nèi)部output設(shè)為true時才有意義,否則歷史緩存機制不會被初始化。
- prompt -要使用的提示字符串。默認為'>'.
- crlfDelay(number):如果\r與\n之間的延遲超過crlfDelay毫秒,則\r與\n都會當(dāng)做換行分隔符。默認為100毫秒。crlfDelay的范圍為[100,2000].
- readline.createInterface()方法創(chuàng)建一個新的readline.Intreface實例,例子如下:
- options:(object)
const readline=require('readline');
const rl=readline.createInterface({
input:process.stdin,
output:process.stdout
});
+ 一旦readline.Interface實例被創(chuàng)建,最常見的就是監(jiān)聽‘line’事件:
rl.on('line',(line)=>{
console.log(`接收到:${line}`);
});
+ 如果該實例的terminal為true,則若它定義了一個output.columns屬性則output流會獲得最佳兼容性,且如果或當(dāng)列發(fā)生變化時,output上觸發(fā)一個'resize'事件(當(dāng)它為一個TTY時,process.stdout會自動處理這個)。
+ completer函數(shù)使用:當(dāng)被調(diào)用時,用戶輸入的當(dāng)前行會被提供給completer函數(shù),并返回一個包含以下兩個條目的數(shù)組:
+ 一個包含自動補全輸入的數(shù)組。
+ 用于匹配的字符串。
+ 例如:[[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函數(shù)結(jié)束兩個參數(shù),則可被異步地調(diào)用。
function completer(linePartial,callback){
callback(null,[['123'],linePartial]);
}
- readline.curorTo(stream,x,y):
- stream(writable):
- x (number):
- y(number):
- readline.cursorTo()方法會移動光標(biāo)到指定的TTY stream中指定的位置。
- readline.emitKeypressEvent(stream[,interface):
- stream(Readable):
- interface(readline.Interface):
- readline.emitKeypressEvents()方法是給定的可寫流stream相應(yīng)接收到的輸入觸發(fā)‘keypress’事件。
- 可選的interface指定了一個readline.Interface實例,用于當(dāng)自動補全被禁用時檢測到復(fù)制黏貼輸入。
- 如果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()方法移動光標(biāo)到給定的TTY stream中相對當(dāng)前的位置。
3.例子:簡單的命令行界面
- 例子,使用readline.Interface類實現(xiàn)一個簡單的命令行界面:
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.逐行讀取文件流:
- 例子,從一個文件系統(tǒng)可讀流中每次一行地消耗輸入:
const readline=require('readline');
const fs=require('fs');
const rl=readline.createInterface({
input:fs.createReadStream('sample.txt');
});
rl.on('line',(line)=>{
console.log('文件的單行內(nèi)容:${line}');
});