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 协议》,转载必须注明作者和本文链接