検索での異体字処理

漢字の検索において、異体字・誤字などを考慮するには、以下の3種類の処理を行う必要があります。

(1) 異体字・誤字・通仮字の「マーク付け」
 テキスト入力時に異体字や誤字の情報をXMLなどによってマーク付けします。そして検索用インデックスの作成時にマークで示される代替テキストの方をインデックス用のテキストとして置き換えます。
(2) 検索対象テキスト・検索キーの「フィルタリング」
 テキスト中の異体字に対し、検索用インデックス作成時および検索キー入力時に、より一般的な漢字に置換したり、異体字選択子などの除去を行います。
(3) 検索時の「複数候補での検索」
 異体字とは言えないものの、よく混同される漢字について、複数の候補で検索をします(「云う」と「言う」など)。

異体字フィルタ(Apache Lucene)

以下は、Apache Lucene にて転置インデックスを作成する際に、異体字をフィルタリングするプログラムの例です。フィルタ "VariantsFilter.java" は、システムプロパティ(cjk.variants)で指定された異体字置換データvariants.txtに基づき、入力テキストのフィルタリングを行います。variants.txtは、本漢字データベースから自動生成した、フィルタ用異体字置換データの一例です。

異体字フィルタ(Unix)

本異体字フィルタは、GNU gperf を用いて、標準入力のUTF-8ストリームに含まれる異体字を変換し、標準出力に出力します。 本プログラムは以下のようにして生成します。

% ruby variants_conv.rb < variants.txt > variants_conv.gp
% gperf -tp variants_conv.gp > variants_conv.c 
  (※ コンピュータの性能にもよりますが、およそ2時間程度の時間がかかります)
% gcc -o variants_conv variants_conv.c

本プログラムを、検索したいテキストにフィルタとして通すと、grep等で常用漢字体で検索可能になります。以下は、“.tar.xz” で圧縮された、UTF-8で符号化された青空文庫の旧字テキストデータに対して、 新字で検索している例です。(--to-command で、$TAR_ARCHIVE環境変数を使用するには、GNU TAR のバージョン1.23以降が必要です)。

% tar --to-command='while read line; do echo $TAR_ARCHIVE:$line; done' \
-xvf "こう・幸田 露伴.tar.xz" | variants_conv | grep "渋い"

上記で検索すると、例えば 「澁い」というテキストが、「渋い」でヒットします。

(出力結果例:)
こう・幸田露伴/骨董_新字旧仮名.txt:は何様だい、なぞと自ら笑つてゐるといふ調子であつた。
かつて聯句を試みたことが有つたが、すべて基調子だから、何も彼も構ふものでは無いので、基の
自由自在で、おもしろいことと雲つたら無かつた。基代り所謂宗匠に視せると、宗匠は苦《にが》い
渋い顔をするもので、基の又宗匠のイヤな顔をするのを面白がつたものであつた。

上記の例では、「其」が「基」に、「云」が「雲」に変換されてしまっています(いずれも古い異体字)。このように、必ずしも適切でない変換も行われるため、検索語に対しても本フィルタを通し、また変換後ではなく変換前の文字列をマッチ文字列として表示するのがよいでしょう。以下はそれを行うサンプルプログラム “variants_grep.rb”の例です。

#!/usr/bin/env ruby -Ku
io=IO.popen("variants_conv", "r+")
io.puts(ARGV[0])
re = Regexp.new(io.gets.chomp)
STDIN.each do |line|
  io.puts line
  if re =~ io.gets then
    print line
  end
end

漢語の抽出と検索

漢文は現代日本語の源流のひとつであり、日本人にとって馴染のある古代語のひとつです。

しかし漢文は漢字・熟語が区切られていない場合が多く、初学者が知らない単語を漢和辞典で引く際に、単語を適切に区切り、辞書で引けるように異体字を正規化するのは面倒でした。

語の区切りが明確でない漢文から単語を抜き出すには、漢和辞典に現れる語彙をトライ木化して、実際の漢文に対して最長一致検索するのが比較的有効なようです。

大規模なトライ木を効率的に作る手法としては、Double-Array や LOUDS が知られています。ここでは、LOUDSを利用したトライ木構築ライブラリ "TX" を使って大漢和辞典の語彙をトライ木化し、実際の漢文に適用する簡単な例を紹介します。

以下は、TxのRubyバインディングである tx-ruby を使って、大漢和辞典の熟語データをトライ木化する例です。ここでは、異体字を片寄せするツールとして、上記の‘variants-conv'を利用しています。

#!/usr/bin/env ruby -Ku

require "tx"

io=IO.popen("variants_conv", "r+")
hash=Hash.new()

STDIN.each do |line|
  line=line.chop
  io.puts line
  line2 = io.gets.chop
  if line2 =~ /^.+DP..... (.[^■]+)$/
    if hash.has_key?($1)
      hash[$1]=hash[$1]+"\n"+line
    else
      hash[$1]=line
    end
  end
end

builder = Tx::MapBuilder.new
hash.each {|key,val|
  builder.add(key,val)
}

builder.build("dkw-word")

# Builds an index and saves it to a file.

以下のサンプルプログラムは、作成したトライ木を使い、標準入力から入力された漢文を大漢和辞典の語彙で検索します。

#!/usr/bin/env ruby -Ku

require "tx"

io=IO.popen("variants_conv", "r+")
map = Tx::Map.open("dkw-word")

STDIN.each do |original|
  original=original.chop
  io.puts original
  converted = io.gets.chop
  print "original =",original ,"\n"
  print "converted=",converted,"\n"
  flag=true
  map.scan(converted).each {|val|
    val[2].split("\n").each{|line|
      if flag 
        print "* "
        flag=false
      else print "  "
      end
      print val[0], " ", line, "\n"
    }
    flag=true
  }
end

以下は、上記のプログラムを、説文解字注の「皇」項に対して適用した例です。本文中から大漢和辞典の語彙にマッチする単語を取り出し、その親字番号と語彙番号(DW)、そして大漢和のページ番号(DP)を表示しています。

original =    <wordhead id="w0050030" img="009b-04">皇</wordhead>
converted=    <wordhead id="w0050030" img="009b-04">皇</wordhead>

original =    <explanation>大也。</explanation>
converted=    <explanation>大也。</explanation>
* 大也 DW05831.0.2217.0 DP02722 大也

original =    <duan_note>見詩毛傳。</duan_note>
converted=    <duan_note>見詩毛伝。</duan_note>
* 毛伝 DW16772.0.0180.0 DP06528 毛傳

original =    <explanation>从自王。</explanation>
converted=    <explanation>従自王。</explanation>

original =    <duan_note>依韵㑹補王字</duan_note>
converted=    <duan_note>依韻会補王字</duan_note>
* 依韻 DW00607.0.0015.0 DP00759 依韻

original =    <explanation>自、始也。始王者、三皇。</explanation>
converted=    <explanation>自、始也。始王者、三皇。</explanation>
* 王者 DW20823.0.0500.0 DP07744 王者
* 三皇 DW00012.0.0369.0 DP00124 三皇

original = <duan_note>王各本譌皇。今正。先鄭注周禮云。四類三皇五
  帝九皇六十四民咸祀之。尚書大傳。燧人爲燧皇。伏羲爲羲皇。神農爲農皇。譙
  周說同。白虎通曰。三皇者何。伏羲、神農、燧人。則改燧人居第三。恐非舊也。
  鄭依春秋緯。伏羲、女媧、神農爲三皇。皇甫謐說同。</duan_note>
converted= <duan_note>王各本叱皇。今正。先鄭注周豊雲。四類三皇五
  帝九皇六十四民鹹祀之。尚書大伝。燧人為燧皇。伏羲為羲皇。神農為農皇。焦
  周説同。白虎通曰。三皇者何。伏羲、神農、燧人。則改燧人居第三。恐非旧也。
  鄭依春秋緯。伏羲、女媧、神農為三皇。皇甫謐説同。</duan_note>
* 先鄭 DW01349.0.0170.0 DP01010 先鄭
* 周豊 DW03441.0.0497.0 DP02023 周禮
* 四類 DW04682.0.0790.0 DP02319 四類
* 三皇五帝 DW00012.0.0372.0 DP00124 三皇五帝
* 九皇 DW00167.0.0187.0 DP00364 九皇
* 六十 DW01453.0.0289.0 DP01142 六十
* 四民 DW04682.0.0732.0 DP02317 四民
* 尚書 DW07493.0.0085.0 DP03504 尙書
* 大伝 DW05831.0.1737.0 DP02706 大傳
* 燧人 DW19469.0.0007.0 DP07455 燧人
* 伏羲 DW00438.0.0026.0 DP00656 伏羲
* 羲皇 DW28552.0.0008.0 DP09443 羲皇
* 神農 DW24673.0.0426.0 DP08593 神農
* 農皇 DW38688.0.0023.0 DP11539 農皇
* 焦周 DW35976.0.0014.0 DP11033 譙周
* 白虎通 DW22678.0.0378.0 DP08148 白虎通
* 三皇 DW00012.0.0369.0 DP00124 三皇
* 伏羲 DW00438.0.0026.0 DP00656 伏羲
* 神農 DW24673.0.0426.0 DP08593 神農
* 燧人 DW19469.0.0007.0 DP07455 燧人
* 燧人 DW19469.0.0007.0 DP07455 燧人
* 居第 DW07663.0.0128.0 DP03544 居第
* 春秋緯 DW13844.0.0334.0 DP05456 春秋緯
* 伏羲 DW00438.0.0026.0 DP00656 伏羲
* 女媧 DW06036.0.0062.0 DP02888 女媧
* 神農 DW24673.0.0426.0 DP08593 神農
* 三皇 DW00012.0.0369.0 DP00124 三皇
* 皇甫謐 DW22701.0.0240.0 DP08217 皇甫謐

original =    <explanation>大君也。</explanation>
converted=    <explanation>大君也。</explanation>
* 大君 DW05831.0.0611.0 DP02666 大君

original = <duan_note>始王天下、是大君也。故號之曰皇。因以爲凡大
  之稱。此說字形㑹意之恉。幷字義訓大之所由來也。皇本大君。因之凡大皆曰皇。
  假借之法準此矣。</duan_note>
converted= <duan_note>始王天下、是大君也。故号之曰皇。因以為凡大
  之称。此説字形会意之恉。並字義訓大之所由来也。皇本大君。因之凡大皆曰皇。
  仮借之法準此矣。</duan_note>
* 天下 DW05833.0.0122.0 DP02741 天下
* 大君 DW05831.0.0611.0 DP02666 大君
* 以為 DW00388.0.0002.0 DP00620 以爲
* 大之 DW05831.0.0918.0 DP02676 大之
* 字形 DW06942.0.0028.0 DP03091 字形
* 会意 DW14306.0.0001.0 DP05640 會意
* 字義 DW06942.0.0020.0 DP03091 字義
* 大之 DW05831.0.0918.0 DP02676 大之
* 所由 DW11715.0.0006.0 DP04705 所由
* 大君 DW05831.0.0611.0 DP02666 大君
* 因之 DW04693.0.0040.0 DP02333 因之
* 仮借 DW00835.0.0079.0 DP00855 假借
  仮借 DW00835.0.0081.0 DP00855 假藉

original =    <explanation>自讀若鼻。</explanation>
converted=    <explanation>自読若鼻。</explanation>
* 読若 DW36088.0.0038.0 DP11053 讀若

original = <duan_note>自下曰鼻也。則自鼻二字爲轉注。此曰自讀若鼻。
  言皇字所从之自讀若鼻。其音同也。</duan_note>
converted= <duan_note>自下曰鼻也。則自鼻二字為転注。此曰自読若鼻。
  言皇字所従之自読若鼻。基音同也。</duan_note>
* 自下 DW30095.0.0057.0 DP09758 自下
* 二字 DW00247.0.0420.0 DP00428 二字
* 転注 DW38507.0.0147.0 DP11502 轉注
* 読若 DW36088.0.0038.0 DP11053 讀若
* 所従 DW11715.0.0066.0 DP04707 所從
* 読若 DW36088.0.0038.0 DP11053 讀若
* 音同 DW43265.0.0070.0 DP12817 音同

original =    <explanation>今俗㠯作始生子爲鼻子是。</explanation>
converted=    <explanation>今俗以作始生子為鼻子是。</explanation>
* 生子 DW21670.0.0135.0 DP07940 生子
* 鼻子 DW48498.0.0025.0 DP13670 鼻子

original = <duan_note>楊氏雄方言曰。鼻、始也。嘼之初生謂之鼻。人
  之初生謂之首。許謂始生子爲鼻子。字本作鼻。今俗乃以自字爲之。徑作自子。
  此可知自與鼻不但義同。而且音同。相假借也。今俗、謂漢時也。鉉本無作字。
  誤。鍇本有。新刻刪之。胡光切。十部。</duan_note>
converted= <duan_note>楊氏雄方言曰。鼻、始也。狩之初生謂之鼻。人
  之初生謂之首。許謂始生子為鼻子。字本作鼻。今俗乃以自字為之。径作自子。
  此可知自与鼻不但義同。能且音同。相仮借也。今俗、謂漢時也。鉉本無作字。
  誤。鍇本有。新刻刪之。瑚光切。十部。</duan_note>
* 楊氏 DW15112.0.0168.0 DP06142 楊氏
* 方言 DW13620.0.0157.0 DP05298 方言
* 之初 DW00125.0.0020.0 DP00344 之初
* 謂之 DW35759.0.0003.0 DP10990 謂之
* 之初 DW00125.0.0020.0 DP00344 之初
* 謂之 DW35759.0.0003.0 DP10990 謂之
* 生子 DW21670.0.0135.0 DP07940 生子
* 鼻子 DW48498.0.0025.0 DP13670 鼻子
* 字本 DW06942.0.0071.0 DP03093 字本
* 為之 DW19686.0.0027.0 DP07480 爲之
* 自与 DW30095.0.0548.0 DP09769 自與
* 能且 DW28871.0.0024.0 DP09521 而且
* 音同 DW43265.0.0070.0 DP12817 音同
* 仮借 DW00835.0.0079.0 DP00855 假借
  仮借 DW00835.0.0081.0 DP00855 假藉
* 無作 DW19113.0.0333.0 DP07345 無作
* 本有 DW14421.0.0013.0 DP05739 本有
* 新刻 DW13572.0.0189.0 DP05271 新刻
* 十部 DW02695.0.0561.0 DP01592 十部

漢字データベースプロジェクトでは、大漢和辞典の熟語データの他に、望月仏教大辞典の熟語データや、中華大辞典の熟語データ等のデータベース化作業を行っています。これらを活用することで、漢文がより読みやすくなる可能性があります。

なお、大漢和辞典や望月仏教大辞典は出版社からは電子化されていませんが、これらの辞書を裁断、スキャンしてPDF化すれば、上記語彙リストから自動的にPDFの当該ページにジャンプさせることもできます。

variants.txtの注意

variants.txtは、UCS漢字の1/3以上を占める異体字を、可能な限り日本の常用漢字へフィルタできるような対応を機械的に生成しています。本表は漢語大字典の異体字表等を参考にしていますが、実際の異体字関係は時代・地域・文脈によって変化します。異体字表は各種の異体字関係が全て混在されており、その中には現在の日本では考えにくいような異体字関係も含まれています。(異体字表の本来の目的は、漢文等の読解の際に通用字などを探すためのものであり、フィルタリング目的で使うのは工夫をしても無理なところがあります。)