簡寫readStream的流動模式並完成文章搜尋功能

圓兒圈圈發表於2019-02-12

搜尋功能實現步驟

1.開啟檔案

2.觸發newListener事件並採用flowing模式讀取資料

3.對資料進行過濾對符合搜尋內容的次數計數

  • 可讀流事實上工作在下面兩種模式之一:flowing 和 paused
  • 在 flowing 模式下, 可讀流自動從系統底層讀取資料,並通過 EventEmitter 介面的事件儘快將資料提供給應用。

呼叫方法

let SearchTime = require(`./SearchTimes`)
let reader = new SearchTime(`./1.txt`,{
    text:`好好`,//搜尋內容
    highWaterMark:30//最高水位線
})

reader.on(`searchTimes`,(data)=>{//監聽searchTimes 返回檔案內容
    console.log(data)
})
reader.on(`end`,data=>{//監聽send 返回檔案滿足條件個數
    console.log(`count`,data)
})

reader.on(`error`,(error)=>{
    console.log(`error`,error)
})
複製程式碼

createReadStream類的flowing模式例項化

  • 引入events,fs。

  • 定義一個SearchTimes類,並繼承event模組為了之後使用事件訂閱釋出,新增傳進來的屬性。

  • 當給一個物件新增一個新的監聽函式時候會觸發newListener事件。

this.on(`newListener`,(type)=>{
            if(type == `searchTimes`){//如果新增了searchTimes和監聽,就開始以flowing模式讀取檔案內容
                this.read();
            }
        })
複製程式碼

實現read方法

  • 判斷檔案是否開啟,為否就觸發一次檔案開啟事件。
  • 根據水位線位置和餘下內容的大小判斷一次讀幾個位元組。
        let howMuchToRead = this.end?Math.min(this.end-this.pos+1,this.highWaterMark):this.highWaterMark
複製程式碼
  • fs.read讀取檔案。
  • 讀取成功,發射searchTimes事件,返回讀取到的內容。
  • 並將讀取到的內容累積記錄。
  • 沒有讀取到或者傳入的end已經大於檔案位移說明讀取完成,執行下一步。
  • 關閉檔案,並用正則過濾問價,返回匹配個數。

測試檔案及程式碼

SearchTimes類

let EventEmitter = require(`events`)
let fs = require(`fs`)

class SearchTimes extends EventEmitter {
    constructor(path, options) {
        super(path, options);
        this.path = path;
        this.text = options.text || ``;
        this.highWaterMark = options.highWaterMark || 64 * 1024;
        this.buffer = Buffer.alloc(this.highWaterMark);
        this.flags = options.flags || `r`;
        this.encoding = options.encoding || `utf-8`;
        this.mode = options.mode || 0o666;
        this.start = options.start || 0;
        this.end = options.end;
        this.pos = this.start;
        this.autoClose = options.autoClose || true;
        this.buffers = ``;
        this.on(`newListener`,(type)=>{
            if(type == `searchTimes`){
                this.read();
            }
        })
        this.open();
    }
    read(){
        if(typeof this.fd != `number`){
            return this.once(`open`,()=>this.read())
        }
        let howMuchToRead = this.end?Math.min(this.end-this.pos+1,this.highWaterMark):this.highWaterMark
        fs.read(this.fd,this.buffer,0,howMuchToRead,this.pos,(err,bytes)=>{
            if(err){
                if(this.autoClose)
                    this.destroy()
                return this.emit(`err`,err)
            }
            if(bytes){
                let data = this.buffer.slice(0,bytes)
                this.pos += bytes
                data = this.encoding?data.toString(this.encoding):data
                this.emit(`searchTimes`,data)
                this.buffers += data
                if(this.end && this.pos > this.end){
                    return this.endFn();
                }else{
                    this.read();
                }
            }else{
                return this.endFn();
            }
       })
    }
    getPlaceholderCount(strSource,text) {
        var thisCount = 0;
        strSource.replace(new RegExp(this.unicode(text),`g`), function (m, i) {
            thisCount++;
        });
        return thisCount;
    }
    unicode(str){
        var value=``;
        for (var i = 0; i < str.length; i++) {
            value += `\u` + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
        }
        return value;
    }
    left_zero_4(str) {
        if (str != null && str != `` && str != `undefined`) {
            if (str.length == 2) {
                return `00` + str;
            }
        }
        return str;
    }
    endFn(){
        const count = this.getPlaceholderCount(this.buffers,this.text)
        this.emit(`end`,count);
        this.destroy();
    }
    open(){
        fs.open(this.path,this.flags,this.mode,(err,fd)=>{
            if(err){
                if(this.autoClose){
                    this.destroy()
                    return this.emit(`err`,err)
                }
            }
            this.fd = fd
            this.emit(`open`)
        })
    }
    destroy(){
        fs.close(this.fd,(err)=>{
            this.emit(`close`)
        })
    }
}

module.exports = SearchTimes
複製程式碼

呼叫檔案

let SearchTime = require(`./SearchTimes`)
let reader = new SearchTime(`./1.txt`,{
    text:`好好`,//搜尋內容
    highWaterMark:30//最高水位線
})

reader.on(`searchTimes`,(data)=>{//監聽searchTimes 返回檔案內容
    console.log(data)
})
reader.on(`end`,data=>{//監聽send 返回檔案滿足條件個數
    console.log(`count`,data)
})

reader.on(`error`,(error)=>{
    console.log(`error`,error)
})
複製程式碼

進行搜尋的物件檔案

好好今天好好裡的梅林強無敵好王哈也不賴好還是咕噠子好好最強好好好賽高
複製程式碼

相關文章