Ruby 求助:怎样将搜狗词库 scel 格式文件转为 txt?

y9info · 2022年07月15日 · 最后由 y9info 回复于 2022年10月31日 · 645 次阅读

目前了解的情况: 1.根据查找的信息,搜狗的 scel 词库就是保存的文本的 unicode 编码,每两个字节一个字符(中文汉字或者英文字母) 2.初步编码如下:

file = "C:/Users/Administrator/Desktop/test/搜狗词汇.scel"
source_file = File.open(file,"rb")

while true
    begin
        by = source_file.sysread(2) 
        p by
    rescue => e
        puts e.message
        break
    end
end
source_file.close

3.上述 2 代码运行显示的结果为: "a\x00" "\xEE\x00" "\x06\x00" "p\x00" "a\x00" "i\x00" "\xEF\x00" "\x06\x00" "p\x00" "a\x00" "n\x00" "\xF0\x00" "\b\x00" "p\x00" "a\x00" "n\x00" "g\x00" "\xF1\x00" "\x06\x00" "p\x00" "a\x00" "o\x00" "\xF2\x00" "\x06\x00" 怎样将上述转码成 txt 文本呢?或者说怎样将上述的字节码转为字符串呢?

我也不会,但可以看别人的代码: https://github.com/howl-anderson/scel2txt

每两个字节一个字符(中文汉字或者英文字母)

utf-16?

import struct
import os
# 拼音表偏移,
startPy = 0x1540

# 汉语词组表偏移
startChinese = 0x2628

# 全局拼音表
GPy_Table = {}

# 解析结果
# 元组(词频,拼音,中文词组)的列表
GTable = []

# 原始字节码转为字符串
def byte2str(data):
    pos = 0
    str = ''
    while pos < len(data):
        c = chr(struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0])
        if c != chr(0):
            str += c
        pos += 2
    return str

# 获取拼音表
def getPyTable(data):
    data = data[4:]
    pos = 0
    while pos < len(data):
        index = struct.unpack('H', bytes([data[pos],data[pos + 1]]))[0]
        pos += 2
        lenPy = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]
        pos += 2
        py = byte2str(data[pos:pos + lenPy])

        GPy_Table[index] = py
        pos += lenPy

# 获取一个词组的拼音
def getWordPy(data):
    pos = 0
    ret = ''
    while pos < len(data):
        index = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]
        ret += GPy_Table[index]
        pos += 2
    return ret

# 读取中文表
def getChinese(data):
    pos = 0
    while pos < len(data):
        # 同音词数量
        same = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]

        # 拼音索引表长度
        pos += 2
        py_table_len = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]

        # 拼音索引表
        pos += 2
        py = getWordPy(data[pos: pos + py_table_len])

        # 中文词组
        pos += py_table_len
        for i in range(same):
            # 中文词组长度
            c_len = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]
            # 中文词组
            pos += 2
            word = byte2str(data[pos: pos + c_len])
            # 扩展数据长度
            pos += c_len
            ext_len = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]
            # 词频
            pos += 2
            count = struct.unpack('H', bytes([data[pos], data[pos + 1]]))[0]

            # 保存
            GTable.append((count, py, word))

            # 到下个词的偏移位置
            pos += ext_len


def scel2txt(in_path,file_name):
    print('-' * 60)
    with open(in_path+file_name, 'rb') as f:
        data = f.read()

    print("词库名:", byte2str(data[0x130:0x338])) # .encode('GB18030')
    print("词库类型:", byte2str(data[0x338:0x540]))
    print("描述信息:", byte2str(data[0x540:0xd40]))
    print("词库示例:", byte2str(data[0xd40:startPy]))

    getPyTable(data[startPy:startChinese])
    getChinese(data[startChinese:])

if __name__ == '__main__':

    # scel所在文件夹路径
    in_path = u"C:/Users/Administrator/Desktop/test/"


    # 输出词典所在文件夹路径
    out_path = u"C:/Users/Administrator/Desktop/test/sogou_dict.txt"

    fin = [fname for fname in os.listdir(in_path) if fname[-5:] == ".scel"]


    # print(fin)
    for f in fin:
        scel2txt(in_path,f)

    # 保存结果
    with open(out_path, 'w', encoding='utf8') as f:
        f.writelines([word+'\n' for count, py, word in GTable])

上面 python 代码是能运行并得到正确结果。 现在想以 ruby 表达出来,以便更深入了解 ruby 的 encode。

def sceltoword(scel_file,out_path)  
  @file = scel_file
  @path = out_path

  source_file = File.open(@file,"rb")
  File.open(@path, 'wt', encoding: 'UTF-8') do |f|
    while true
        begin
            by = source_file.sysread(2) 
            by.force_encoding("utf-16le").encode!("utf-8")
            f.syswrite(by)
        rescue => e
            puts e.message
            break
        end
    end
  end 
  source_file.close
  File.open(@path,"r") do |lines|
    buffer = lines.read.scan(/\p{Han}+/u) 
    buffer.reject! {|word| word.size < 2 } 
    File.open(@path,"w") do |l|
      l.puts(buffer) 
    end
  end  
end

使用了一个投机取巧的办法,使用正则表达式,把 UTF-16 转成 UTF-8 之后的文件中的中文短语部分提取出来,就得到了想要的词汇 txt 文件。 这个办法不完美,无法精确对应每个短语的拼音,但是解决了提取搜狗词库中的中文 txt 短语的目的。 由于搜狗词汇庞大的词汇量,所以提取的词汇,可以用于关键词检索中的自动输入、自然语义分析的关键词、倒排索引等用途。应用场景如:通过提取法律词汇,建立关键词与法律法律法规条文的对应关系,根据法律关键词快速查找合适准确的法律条文。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号