go 自定義二進位制檔案讀寫-儲存倒排索引文件 id

littlexiaoshuishui發表於2020-07-14

搜尋引擎中一個非常重要的資料結構就是倒排索引,類似下表:

go自定義二進位制檔案讀寫-儲存倒排索引文件id
倒排索引可以分成兩部分,關鍵詞和和文件編號。文件編號資料我們可以使用一個大檔案來儲存。類似下面:

go自定義二進位制檔案讀寫-儲存倒排索引文件id
記錄好偏移量。而關鍵詞我們使用另外一種資料結構儲存,並記錄文件編號的檔案偏移量和編號數量,只要查詢到關鍵詞,就能通過偏移量獲取對應的文件編號。

文件編號使用int64儲存,但最終寫入到檔案是用二進位制來儲存,Go語言的 encoding/binary 包中的 binary.Write() 函式使得以二進位制格式寫資料非常簡單。封裝了一些介面方便我們讀寫int64

func main() {
    h := util.NewFileHandler("test")//會建立test.bin檔案
    index := h.WriteInt64(998,0) //寫入998,返回偏移量
    num := h.ReadInt64(index) //傳入偏移量,讀取一個int64
    s := h.ReadDocIdsArry(index,100) //傳入偏移量,讀取100個int64
    fmt.Println(index,num,s)
}
package util

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "io"
    "os"
)

type FileHandler struct{
    filePath string
    file *os.File
}
const FILEDIR = "/data/index/"

//一個欄位一個檔案
func NewFileHandler(field string) *FileHandler{
    root := GetPath()
    filePath := root+FILEDIR+field+".bin"
    var fp *os.File
    var err error
    if FileExist(filePath) {
        fp,err = os.OpenFile(filePath, os.O_RDWR, 0666)
        if err != nil {
            fmt.Println("open file:",err)
        }
    }else{
        fp,err = os.Create(filePath)
        if err != nil {
            fmt.Println("create file:",err)
        }
    }
    fileHandler := new(FileHandler)
    fileHandler.filePath = filePath
    fileHandler.file = fp
    return fileHandler
}

//從指定的位置讀取一個int64
func (fh *FileHandler)ReadInt64(start int64) int64{
    buf := make([]byte,8)
    _,err := fh.file.ReadAt(buf, start)
    if err!=nil{
        if err==io.EOF {
            return -1
        }
    }
    return     bytetoint(buf) //把讀取的位元組轉為int64
}

//指定的地方寫入int64,不傳就獲取檔案最後的下標
func (fh *FileHandler)WriteInt64(value,start int64) int64{
    if start < 1 {
        start,_ = fh.file.Seek(0, io.SeekEnd) //表示0到檔案end的偏移量
    }
    b := inttobyte(value)
    _,err := fh.file.WriteAt(b,start) //n表示寫入的位元組數,data是int64,所以n=8, 使用writeAt不能使用追加模式
    if err!= nil{
        fmt.Println(err)
    }
    return start
}

//從start下標讀取len個int64
func (fh *FileHandler)ReadDocIdsArry(start, len int64) []int64{
    var i int64 = 0
    res := make([]int64,0,len)
    for ; i < len; i++{
        start = start + i*8
        num := fh.ReadInt64(start)
        if num <= 0 { //越界了就直接返回
            break
        }
        res = append(res,num)
    }
    return res
}

func FileExist(filePath string) bool{
    _, err := os.Stat(filePath)
    if err != nil {
        if os.IsNotExist(err) {
            return false
        }
    }
    return true
}

// []byte 轉化 int64
func bytetoint(by []byte)  int64{
    var num int64
    b_buf := bytes.NewBuffer(by)
    binary.Read(b_buf, binary.BigEndian, &num)
    return num
}
// int64 轉 []byte
func inttobyte(num int64) []byte {
    b_buf := new(bytes.Buffer)
    binary.Write(b_buf, binary.BigEndian,&num) //num型別不能是int
    return b_buf.Bytes()
}

//獲取當前程式目錄
func GetPath() string{
    path,_ := os.Getwd()
    return path
}

注意:

使用binary包時,必須使用明確的長度確定的型別,可以用int32,int64 但別用int。
原因:www.jianshu.com/p/219c24a4347c

func Write(w io.Writer, order ByteOrder, data interface{})
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章