Skip to content

Repository files navigation

mecab-dic-converter

MeCab 用のコンパイル済み辞書を読み取り、解析・再構成し、最終的に Vibrato や Lindera のような Mecab互換 tokenizer が使える辞書へ変換するための Rust crate です。

現時点では、UTF-8 の compiled MeCab 0.996 系辞書を対象にしています。

Acknowledgements

本リポジトリの検討と実装にあたっては、アドバイザーとして支援していただいた帝京大学 文学部 日本文化学科 講師 梅田径 先生に感謝します。

公開リポジトリとしての注意

  • このリポジトリには、手元検証で使っている mecab-dicts/, mecab-0.996/, samples/ などのローカル作業用ディレクトリは含まれていません
  • README や examples に出てくるそれらのパスは、開発時の既定値または検証用パスです。公開版を clone した環境では、利用者が自分で辞書や比較用データを用意する前提です
  • 辞書データやその再配布条件は upstream ごとに異なります。この crate 自体とは別に、入力辞書・生成物・比較用データのライセンスを確認してください
  • 特に unidic 系辞書は系統ごとに条件が異なります。Ver.2.x 以降の現代書き言葉 UniDic など triple-license のものもありますが、歴史系・小説系を含む他の UniDic には商用利用前の事前相談や成果公表時の明記が求められるものがあります

できること

  • sys.dic / unk.dic / matrix.bin / char.bin / dicrc のパース
  • compiled 辞書から lex.csv / unk.def / char.def / matrix.def 相当の再構成
  • 辞書全体 CSV や lookup 結果 CSV のデバッグ出力
  • vibrato-rkyv 形式の system.dic.zst 生成
  • MeCab と Vibrato の形態素解析結果比較

現在のスコープ

  • 対象は UTF-8 の compiled MeCab 0.996 辞書に限定しています
  • decoder -> resolver -> encoder の分離を前提に実装しています
  • フル辞書の system.dic.zst 生成は動作します
  • UniDic 全件での MeCab / Vibrato 完全一致はまだ調整中です

前提環境

  • Rust / Cargo
  • Linux などの POSIX 環境を推奨
  • Windows でも CLI の build は可能です。release CLI は GitHub Actions の windows-latest でも build しています
  • Windows で source build する場合は Git と Visual Studio C++ Build Tools (MSVC) が必要です
  • MeCab CLI 比較 example を使う場合は mecab コマンドが必要

比較 example 以外も、実行には compiled MeCab 辞書一式を別途用意する前提です。

セットアップ

依存解決と基本テストは次で確認できます。

cargo test

vibrato-rkyv への変換機能を使う場合は vibrato-export feature を有効にします。

cargo test --features vibrato-export

Windows で source build する

別の Windows マシンで source から build する場合は、少なくとも次を用意してください。

  1. Git
  2. Visual Studio 2022 Build Tools
  3. Rustup と stable の x86_64-pc-windows-msvc toolchain
  4. GitHub / crates.io にアクセスできるネットワーク

vibrato-rkyv は git pin した fork に依存しているため、fresh な環境では依存取得のためにネットワークが必要です。

Visual Studio Build Tools は Desktop development with C++ を有効にし、少なくとも MSVC toolchain と Windows SDK を入れてください。

セットアップ後の確認手順は次です。

rustup default stable-x86_64-pc-windows-msvc
cargo test
cargo test --features vibrato-export

lindera-export を含めた release CLI の build は次で確認できます。

cargo build --release --features "vibrato-export lindera-export" --bin mecab-dic-converter

通常の PowerShell で link.exe を見失う場合は、Developer PowerShell for VS 2022 から同じコマンドを実行してください。

MeCab との比較 example を Windows でも使いたい場合は、別途 mecab.exe をインストールして PATH に通す必要があります。比較以外の parser / resolver / encoder 系コマンドは、repo 内の辞書ファイルだけで実行できます。

source build ではなく CLI を実行するだけなら、GitHub Releases か workflow artifact の Windows 向け mecab-dic-converter.exe を使う方法もあります。

CLI と配布

library に加えて、1 本の CLI バイナリとしても使えるようにしました。ローカル build は次です。

cargo build --release --features "vibrato-export lindera-export" --bin mecab-dic-converter

生成物は Linux なら target/release/mecab-dic-converter、Windows なら target\release\mecab-dic-converter.exe です。

GitHub Actions には release-cli.yml を追加してあり、 workflow_dispatchv* tag push で Linux / Windows の release artifact を生成します。

CLI は compiled MeCab 辞書ルートを明示的に渡す前提です。help は次で確認できます。

cargo run --release --features "vibrato-export lindera-export" -- --help

以下の例で使っている mecab-dicts/unidic-novel-v202512 はローカル検証用のパス名です。公開リポジトリには含まれないため、手元の辞書配置に置き換えてください。

例えば lookup は次です。

cargo run --release --features "vibrato-export lindera-export" -- \
  lookup \
  --dictionary-root mecab-dicts/unidic-novel-v202512 \
  京都東京都

Vibrato 用辞書の生成は次です。

cargo run --release --features "vibrato-export lindera-export" -- \
  build-vibrato \
  --dictionary-root mecab-dicts/unidic-novel-v202512 \
  --output target/system-full.dic.zst

Lindera 用 compiled directory の生成は次です。

cargo run --release --features "vibrato-export lindera-export" -- \
  build-lindera \
  --dictionary-root mecab-dicts/unidic-novel-v202512 \
  --output target/lindera-system-dic

License

このリポジトリ内のコードは MIT OR Apache-2.0 で提供します。詳細は LICENSE-MITLICENSE-APACHE を参照してください。

ただし、次はこのリポジトリのコードライセンスとは別に扱ってください。

  • 外部辞書データ
  • 外部辞書から生成した成果物
  • patches/git-deps/ に含まれる upstream 差分

これらの扱いは THIRD_PARTY.md に整理しています。

クイックスタート

1. lookup 結果を CSV で見る

入力文字列に対して、辞書引き結果を MeCab 風の CSV で表示します。

cargo run --example dump_lookup_csv -- 京都東京都

辞書ルートを明示する場合:

cargo run --example dump_lookup_csv -- mecab-dicts/unidic-novel-v202512 京都東京都

2. 辞書全体を CSV でダンプする

system / unknown / both を指定できます。省略時は both です。

cargo run --example dump_dictionary_csv -- both > target/dictionary.csv
cargo run --example dump_dictionary_csv -- mecab-dicts/unidic-novel-v202512 system > target/system.csv

3. char.defmatrix.def を再構成する

cargo run --example dump_char_def > target/char.def
cargo run --example dump_matrix_def -- nonzero > target/matrix.def

dump_matrix_def の第 2 引数は all または nonzero です。省略時は nonzero です。

Vibrato 辞書を作る

フル辞書の system.dic.zst を生成します。大きい辞書では debug build がかなり遅いので、release build を推奨します。

cargo run --release --example build_system_dic_zst --features vibrato-export -- \
  mecab-dicts/unidic-novel-v202512 \
  target/system-full.dic.zst

引数を省略すると、辞書ルートは mecab-dicts/unidic-novel-v202512、出力先は system.dic.zst になります。

現在の検証環境では、checked-in UniDic のフル変換はおおむね次の規模です。

  • 所要時間: 約 9 分
  • 出力サイズ: 約 968 MB
  • 最大 RSS: 約 7.8 GB

値は実行環境に依存します。

zstd 圧縮 worker 数は既定で利用可能 CPU 数と 8 の小さい方を使います。必要なら MECAB_DIC_CONVERTER_ZSTD_WORKERS で上書きできます。

MECAB_DIC_CONVERTER_ZSTD_WORKERS=16 \
cargo run --release --example build_system_dic_zst --features vibrato-export -- \
  mecab-dicts/unidic-novel-v202512 \
  target/system-full.dic.zst

Lindera 辞書ディレクトリを作る

Lindera がそのまま読める compiled dictionary directory を生成します。内部では一時ディレクトリに lex.csv / char.def / unk.def / matrix.def を再構成し、lindera-dictionaryDictionaryBuildermetadata.json と各 .bin / .mtx / .da を生成します。

cargo run --release --example build_lindera_system_dic --features lindera-export -- \
  mecab-dicts/unidic-novel-v202512 \
  target/lindera-system-dic

第 1 引数の辞書ルートは必須です。出力先を省略した場合は target/lindera-system-dic を使います。

出力先には少なくとも次の Lindera runtime files が並びます。

  • metadata.json
  • dict.da
  • dict.vals
  • dict.wordsidx
  • dict.words
  • matrix.mtx
  • char_def.bin
  • unk.bin

checked-in の compiled UniDic は Lindera 既定の UniDic schema より feature 列が多いため、 この exporter は辞書内の実列数に合わせた custom metadata.json を自動生成します。

ただし Lindera 3.0.5 の matrix.mtx format は context size を signed 16-bit で保持するため、 matrix.bin の right/left context size が 32767 を超える辞書は compiled Lindera directory に 変換できません。checked-in の mecab-dicts/unidic-novel-v202512right=39687, left=35597 なので、この辞書をそのまま Lindera 向けに export することはできません。

MeCab と Vibrato を比較する

小さなテキストだけで部分辞書を組んで比較する

入力テキストに必要な語だけを拾って小さな辞書を in-memory で組み、MeCab と比較します。

cargo run --release --example compare_mecab_vibrato --features vibrato-export -- 京都東京都

フル辞書で比較する

フル辞書 artifact があればそれを使い、なければ in-memory build にフォールバックして比較します。

cargo run --release --example compare_mecab_vibrato_full --features vibrato-export -- \
  mecab-dicts/unidic-novel-v202512 \
  target/system-full.dic.zst \
  samples/takekurabe.txt \
  50

出力は target/*.mecab.tsvtarget/*.vibrato.tsv に保存されます。

ライブラリとして使う

この crate は大きく 3 層に分かれています。

  • decoder: compiled バイナリをそのまま読む層
  • resolver: lex.csvmatrix.def 相当を再構成する層
  • encoder: Vibrato 向け辞書へ変換する層

最小の利用例は次のようになります。

use std::{fs::File, path::Path};

use mecab_dic_converter::{CharDefResolver, CompileInputResolver, MatrixDefResolver};

#[cfg(feature = "vibrato-export")]
use mecab_dic_converter::VibratoRkyvEncoder;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let root = Path::new("mecab-dicts/unidic-novel-v202512");

    let compile_inputs = CompileInputResolver::open(root)?;
    let mut lex_csv = Vec::new();
    compile_inputs.write_lex_csv(&mut lex_csv)?;

    let char_def = CharDefResolver::open(root)?;
    let matrix_def = MatrixDefResolver::open(root)?;

    let _ = (char_def, matrix_def);

    #[cfg(feature = "vibrato-export")]
    {
        let encoder = VibratoRkyvEncoder::open(root)?;
        let file = File::create("system.dic.zst")?;
        encoder.write_dictionary_zstd(file)?;
    }

    Ok(())
}

公開 API は src/lib.rs から re-export しています。

変換進捗を UI に流したい場合は、write_dictionary_zstd_with_progress(...) を使うと callback で stage ごとの進捗を受け取れます。

checked-in の build_system_dic_zst.rs example もこの progress API を使うようにしてあり、実行すると stderr に現在の stage と進捗率が表示されます。

use std::{fs::File, path::Path};

use mecab_dic_converter::{
  VibratoRkyvEncodeStage, VibratoRkyvEncodeProgress, VibratoRkyvEncoder,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
  let root = Path::new("mecab-dicts/unidic-novel-v202512");
  let encoder = VibratoRkyvEncoder::open(root)?;
  let file = File::create("system.dic.zst")?;

  encoder.write_dictionary_zstd_with_progress(file, |progress: VibratoRkyvEncodeProgress| {
    let stage = match progress.stage() {
      VibratoRkyvEncodeStage::ReconstructLexCsv => "lex.csv",
      VibratoRkyvEncodeStage::ReconstructCharDef => "char.def",
      VibratoRkyvEncodeStage::ReconstructUnkDef => "unk.def",
      VibratoRkyvEncodeStage::BuildDictionary => "dictionary build",
      VibratoRkyvEncodeStage::BuildMatrixConnector => "matrix connector",
      VibratoRkyvEncodeStage::SerializeDictionary => "serialize",
      VibratoRkyvEncodeStage::CompressDictionary => "zstd",
      VibratoRkyvEncodeStage::Finished => "finished",
    };

    if let Some(fraction) = progress.fraction() {
      eprintln!("{stage}: {:.1}%", fraction * 100.0);
    } else {
      eprintln!("{stage}: in progress");
    }
  })?;

  Ok(())
}

desktop app 側で closure を避けたい場合は、標準の std::sync::mpsc::Sender をそのまま渡せる sender wrapper も使えます。receiver が先に閉じても変換処理自体は継続します。

use std::{fs::File, path::Path, sync::mpsc};

use mecab_dic_converter::{VibratoRkyvEncodeStage, VibratoRkyvEncoder};

fn main() -> Result<(), Box<dyn std::error::Error>> {
  let root = Path::new("mecab-dicts/unidic-novel-v202512");
  let encoder = VibratoRkyvEncoder::open(root)?;
  let file = File::create("system.dic.zst")?;
  let (sender, receiver) = mpsc::channel();

  std::thread::spawn(move || {
    while let Ok(progress) = receiver.recv() {
      if progress.stage() == VibratoRkyvEncodeStage::Finished {
        break;
      }
      eprintln!(
        "{:?}: {} / {:?}",
        progress.stage(),
        progress.completed(),
        progress.total(),
      );
    }
  });

  encoder.write_dictionary_zstd_with_progress_sender(file, &sender)?;
  Ok(())
}

ReconstructLexCsv / ReconstructUnkDef / BuildMatrixConnector / CompressDictionary は determinate progress を返します。BuildMatrixConnectormatrix.bin から dense な connector data を直接組み立てる段階です。BuildDictionary も以前の開始・完了だけではなく、lex.csv parse、lexicon index 構築、検証などの途中経過を返すようになりました。なお BuildDictionaryunittotal は sub-phase ごとに切り替わるため、UI 側では stage だけでなく unit も見て扱う前提です。

example 一覧

  • build_system_dic_zst.rs: フル辞書を system.dic.zst に変換し、stderr に進捗を表示
  • compare_mecab_vibrato.rs: 部分辞書で MeCab / Vibrato を比較
  • compare_mecab_vibrato_full.rs: フル辞書で MeCab / Vibrato を比較
  • dump_char_def.rs: char.def を再構成して標準出力へ出す
  • dump_dictionary_csv.rs: 辞書全体 CSV を出力
  • dump_lookup_csv.rs: 入力文字列に対する候補 CSV を出力
  • dump_matrix_def.rs: matrix.def を再構成して標準出力へ出す
  • profile_build_stages.rs: 再構成 / builder / zstd 圧縮の段階別時間を計測

既知の制約

  • 対象外のエンコーディングや MeCab バージョンには未対応です
  • vibrato-rkyv / crawdad-rkyv は git pin した fork に依存しており、matrix.bin から dense な matrix costs を直接 builder に渡しています
  • フル辞書変換はメモリ使用量が大きめです
  • checked-in UniDic での full comparison には未解決の差分が残っています

よく使うコマンド

cargo test
cargo test --features vibrato-export
cargo run --example dump_lookup_csv -- 京都東京都
cargo run --example dump_dictionary_csv -- both > target/dictionary.csv
cargo run --example dump_char_def > target/char.def
cargo run --example dump_matrix_def -- nonzero > target/matrix.def
cargo run --release --example build_system_dic_zst --features vibrato-export -- mecab-dicts/unidic-novel-v202512 target/system-full.dic.zst
cargo run --release --example compare_mecab_vibrato_full --features vibrato-export -- mecab-dicts/unidic-novel-v202512 target/system-full.dic.zst samples/takekurabe.txt 50

About

MeCab 用のコンパイル済み辞書を読み取り、解析・再構成し、最終的に Vibrato や Lindera のようなMecab互換 tokenizer が使える辞書へ変換するための Rust crate です。

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages