其他分享
首页 > 其他分享> > 输入法词库解析(一)百度自定义方案.def

输入法词库解析(一)百度自定义方案.def

作者:互联网

参考了 asd1fque1 的词库处理工具 js 实现

解析

码表偏移量 0x6D

占用字节数 描述
1 编码长度(红色框)
1 词长 * 2 + 2
由编码长度决定 编码(黄色框),可以是纯编码,也可以是 编码=位置
由词长决定 词(绿色框),utf16-le 编码
6 6 个空字节代表词条结束

golang 实现:

func ParseBaiduDef(rd io.Reader) Dict {
    ret := make(Dict, 1e5)       // 初始化
    tmp, _ := ioutil.ReadAll(rd) // 全部读到内存
    r := bytes.NewReader(tmp)
    r.Seek(0x6D, 0) // 从 0x6D 开始读
    // utf-16le 转换
    decoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
    for {
        codeLen, err := r.ReadByte() // 编码长度
        wordLen, err := r.ReadByte() // 词长*2 + 2
        if err != nil {
            break
        }
        sliCode := make([]byte, int(codeLen))
        sliWord := make([]byte, int(wordLen)-2) // -2 后就是字节长度,没有考虑4字节的情况

        r.Read(sliCode) // 编码切片
        r.Read(sliWord)

        code := string(sliCode)
        word, _ := decoder.Bytes(sliWord)
        ret.insert(strings.Split(code, "=")[0], string(word))

        r.Seek(6, 1) // 6个00,1是相对当前位置
    }
    return ret
}

生成

码表部分和解析一样的,没什么好说的。

主要考虑前 0x6C(109) 个字节。

第一个字节意义不明,可能是最大码长(一般是 0,有的码表里是 4)

后面每 4 字节一组,共 27 组。

表示以 26 个首字母开头词条的字节长度累加(不包括前 2 个表示长度的字节,包括后 6 个 0)

计算时,统计每个首字母的长度累计,写入时再次累加。

golang 实现:


func GenBaiduDef(dl []codeAndWords) []byte {
    var buf bytes.Buffer
    // 首字母词条字节数统计
    lengthMap := make(map[byte]int)
    buf.Write(make([]byte, 0x6D, 0x6D))
    // utf-16le 转换
    encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
    for _, v := range dl {
        code := v.code
        for i, word := range v.words {
            if i != 0 { // 不在首选的写入位置信息,好像没什么用?
                code = v.code + "=" + strconv.Itoa(i+1)
            }
            sliWord, _ := encoder.Bytes([]byte(word)) // 转为utf-16le
            buf.WriteByte(byte(len(code)))            // 写编码长度
            buf.WriteByte(byte(len(sliWord) + 2))     // 写词字节长+2
            buf.WriteString(code)                     // 写编码
            buf.Write(sliWord)                        // 写词
            buf.Write([]byte{0, 0, 0, 0, 0, 0})       // 写6个0

            // 编码长度 + 词字节长 + 6,不包括长度本身占的2个字节
            lengthMap[code[0]] += len(code) + len(sliWord) + 2 + 6
        }
    }

    // 文件头
    byteList := make([]byte, 0, 0x6D)
    byteList = append(byteList, 0) // 第一个字节可能是最大码长?
    // 长度累加
    var currNum int
    for i := 0; i <= 26; i++ {
        currNum += lengthMap[byte(i+0x60)]
        // 不知道怎么来的,反正就这样算
        currBytes := []byte{byte(currNum % 0x100), byte((currNum / 0x100) % 0x100),
            byte((currNum / 0x10000) % 0x100), byte((currNum / 0x1000000) % 0x100)}
        byteList = append(byteList, currBytes...)
    }
    // 替换文件头
    ret := buf.Bytes()
    for i := 0; i < len(byteList); i++ {
        ret[i] = byteList[i]
    }
    return ret
}

标签:编码,输入法,code,字节,自定义,词库,byte,buf,sliWord
来源: https://www.cnblogs.com/cxcn/p/16314737.html