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