imewlconverter icon indicating copy to clipboard operation
imewlconverter copied to clipboard

百度输入法的个人词库导出只能是bin

Open zhanggene opened this issue 2 years ago • 3 comments

首先非常肯定楼主的工具很好用,最近遇到个问题,我平时使用百度输入法的批量造词功能,批量创建个人词库。然后倒入到win10的输入法中使用,最近百度的新版本(好久不更新,最近又开始更新了),导出的词库只支持bin格式。所以楼主是否可以支持百度输入法的bin格式的转换 。非常感谢 百度输入法的个人词库导出只能是bin

zhanggene avatar Dec 14 '21 09:12 zhanggene

现在的输入法也很鸡贼,之前都是可以导出TXT的,现在不行了

miaozilong avatar Mar 14 '22 14:03 miaozilong

已研究但不清楚是什么编码方式。肯定没有加密或压缩算法。可能某种进制或移位。可能需要反编译大佬。

case.zip 附件是测试时生成的样例文件,不熟悉hex转换的就不用下载了。万一有大佬出现呢<_<

yfdyh000 avatar Mar 26 '22 08:03 yfdyh000

用21年版导出

gary8177 avatar Jan 26 '24 10:01 gary8177

用21年版导出

导出不了

PandaFiredoge avatar Mar 02 '24 15:03 PandaFiredoge

现在有办法吗?

PandaFiredoge avatar Mar 19 '24 08:03 PandaFiredoge

经过分析和查找,在网上找到了编码的算法,来源是分析他们浏览器的《某度base64魔改算法还原》。

文章里的分析和代码太乱了,用 Python 重写了一下,在这里贴一下解码的部分。Python 3.8 以上调用 decode_bin() 即可导出 UTF-16LE 的文本格式。

MASK = 0x2D382324
TABLE = b"qogjOuCRNkfil5p4SQ3LAmxGKZTdesvB6z_YPahMI9t80rJyHW1DEwFbc7nUVX2-"
DECODE_TABLE = bytes.maketrans(TABLE, bytes(range(64)))

def decode(data: bytes):
    assert len(data) % 4 == 2
    assert (base64_remainder := data[-2] - 65) in {0, 1, 2} and data[-1] == 0

    data = data.translate(DECODE_TABLE)
    transformed = bytearray()

    for i in range(0, len(data) - 2, 4):
        high_bits = data[i + 3]
        transformed += bytes((
            data[i] | (high_bits & 0b110000) << 2,
            data[i + 1] | (high_bits & 0b1100) << 4,
            data[i + 2] | (high_bits & 0b11) << 6,
        ))

    if base64_remainder:
        for i in range(3 - base64_remainder):
            assert transformed.pop() == 0

    result = bytearray()

    for i in range(0, len(transformed) // 4 * 4, 4):
        chunk = MASK ^ int.from_bytes(transformed[i : i + 4], byteorder="little")
        chunk = (chunk & 0x1FFFFFFF) << 3 | chunk >> 29
        result += chunk.to_bytes(4, byteorder="little")

    if remainder := transformed[i + 4 :]:
        chunk = MASK ^ int.from_bytes(remainder, byteorder="little")
        result += chunk.to_bytes(4, byteorder="little")[: len(remainder)]

    return result


def decode_bin(input_path: str, output_path: str):
    assert input_path.endswith(".bin") and output_path.endswith(".txt")
    with open(input_path, "rb") as f, open(output_path, "wb") as output:
        output.write(b"\xff\xfe")
        f.seek(1)
        for line in f:
            if data := line[1:-1]:
                output.write(decode(data))
                output.write(b"\n\0")

stevenlele avatar Mar 21 '24 01:03 stevenlele

经过分析和查找,在网上找到了编码的算法,来源是分析他们浏览器的《某度base64魔改算法还原》。

文章里的分析和代码太乱了,用 Python 重写了一下,在这里贴一下解码的部分。Python 3.8 以上调用 decode_bin() 即可导出 UTF-16LE 的文本格式。

MASK = 0x2D382324
TABLE = b"qogjOuCRNkfil5p4SQ3LAmxGKZTdesvB6z_YPahMI9t80rJyHW1DEwFbc7nUVX2-"
DECODE_TABLE = bytes.maketrans(TABLE, bytes(range(64)))

def decode(data: bytes):
    assert len(data) % 4 == 2
    assert (base64_remainder := data[-2] - 65) in {0, 1, 2} and data[-1] == 0

    data = data.translate(DECODE_TABLE)
    transformed = bytearray()

    for i in range(0, len(data) - 2, 4):
        high_bits = data[i + 3]
        transformed += bytes((
            data[i] | (high_bits & 0b110000) << 2,
            data[i + 1] | (high_bits & 0b1100) << 4,
            data[i + 2] | (high_bits & 0b11) << 6,
        ))

    if base64_remainder:
        for i in range(3 - base64_remainder):
            assert transformed.pop() == 0

    result = bytearray()

    for i in range(0, len(transformed) // 4 * 4, 4):
        chunk = MASK ^ int.from_bytes(transformed[i : i + 4], byteorder="little")
        chunk = (chunk & 0x1FFFFFFF) << 3 | chunk >> 29
        result += chunk.to_bytes(4, byteorder="little")

    if remainder := transformed[i + 4 :]:
        chunk = MASK ^ int.from_bytes(remainder, byteorder="little")
        result += chunk.to_bytes(4, byteorder="little")[: len(remainder)]

    return result


def decode_bin(input_path: str, output_path: str):
    assert input_path.endswith(".bin") and output_path.endswith(".txt")
    with open(input_path, "rb") as f, open(output_path, "wb") as output:
        output.write(b"\xff\xfe")
        f.seek(1)
        for line in f:
            if data := line[1:-1]:
                output.write(decode(data))
                output.write(b"\n\0")

很棒,您有兴趣提 pr 吗

nopdan avatar Mar 21 '24 04:03 nopdan

很棒,您有兴趣提 pr 吗

我没怎么写过 C# 也没有环境,对项目也不熟悉,如果你方便的话就转写一下吧。上面有别人上传的测试的文件,如果需要的话我也可以提供解码后的格式样本。

如果可以的话麻烦在提交信息里面加上一行:

Co-authored-by: stevenlele <[email protected]>

stevenlele avatar Mar 21 '24 07:03 stevenlele

非常感谢各位大佬🙏

PandaFiredoge avatar Mar 21 '24 11:03 PandaFiredoge

很棒,您有兴趣提 pr 吗

我没怎么写过 C# 也没有环境,对项目也不熟悉,如果你方便的话就转写一下吧。上面有别人上传的测试的文件,如果需要的话我也可以提供解码后的格式样本。

如果可以的话麻烦在提交信息里面加上一行:

Co-authored-by: stevenlele <[email protected]>

我已经提交了

nopdan avatar Mar 21 '24 13:03 nopdan

https://github.com/studyzy/imewlconverter/blob/3012c564f282da6fafed721cd27599397013c244/src/ImeWlConverterCore/IME/BaiduPinyinBackup.cs#L31-L32

程序确实是按照 <cnword><enword><sysusrword> 的顺序导出的,应该不会出现乱序。

stevenlele avatar Mar 21 '24 14:03 stevenlele

然后英文词的格式是:

API	0	0	4	1707932168

分隔符是 \t,词频目测是 [3],至于 [1][2] 的意义不明确。我这里 [1] 有少量的 1、2、5 等值,[2] 都是 0。

stevenlele avatar Mar 21 '24 14:03 stevenlele

https://github.com/studyzy/imewlconverter/blob/3012c564f282da6fafed721cd27599397013c244/src/ImeWlConverterCore/IME/BaiduPinyinBackup.cs#L31-L32

程序确实是按照 <cnword><enword><sysusrword> 的顺序导出的,应该不会出现乱序。

虽然我测试的也都是这个顺序,但也不能排除别的情况,这样写可以无视顺序。

nopdan avatar Mar 22 '24 07:03 nopdan

然后英文词的格式是:

API	0	0	4	1707932168

分隔符是 \t,词频目测是 [3],至于 [1][2] 的意义不明确。我这里 [1] 有少量的 1、2、5 等值,[2] 都是 0。

目前的程序对一种格式只能指定一种 CodeType,无法同时导入拼音和英文。

nopdan avatar Mar 22 '24 07:03 nopdan