go 自定义二进制文件读写-保存倒排索引文档 id

搜索引擎中一个非常重要的数据结构就是倒排索引,类似下表:

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 协议》,转载必须注明作者和本文链接
用过哪些工具?为啥用这个工具(速度快,支持高并发...)?底层如何实现的?
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!