部首共通の熟語を見つけるプログラム
はじめに
なんかTwitterで「○○さんがいいねしました」的なやつでTLにこんなツイートが流れてきた訳ですよ。
【求ム】このツイートのリプ欄に部首が同じ漢字でのみ構成される熟語を思いつくだけぶら下げてください。文字数不問。
— shu (@Ai_Together) 2019年4月10日
許諾?僥倖?魑魅魍魎?そのあたりが思いつきます。んで私がこんなことをつぶやくわけですよ。
部首同一の熟語、プログラム書いて調べるのが賢そう。
— Gongon (@uo6uo6) April 10, 2019
で試してみたら意外と上手くいったので共有しようという流れです。
熟語データの取得
まず、熟語の一覧表的なものが欲しいわけです。でググってみるといい感じの百科辞書のサービスがあったので借りました。神。
リンク先の一番下にあ行~わ行までのデータをテキストファイルのlzhファイルで配布されてるのでダウンロード。Windows10では標準でlzhが解凍できないので適当なオンラインサービスで解凍。「jisho」って名前のフォルダ下に解凍したフォルダを配置。jishoフォルダと同ディレクトリにPowerShellで移動して
> Get-ChildItem ".\jisho" -Recurse -File -Filter "*" | Get-Content | Add-Content "jisho.txt"
で下位フォルダのテキストファイルをjisho.txtにまとめる。
(PowerShellでテキストファイルを結合する - Qiitaからコピペ)
多分文字コードがSJISになってるので手動でUTF-8に変換した。
熟語データの整形
jisho.txtの中身を見てみると丁寧にいろいろ書いてくれてますね。よく見ると欲しいのは【】で囲まれた部分の中身だと分かります。親切にも取り出しやすい形になってます。神。
import re with open('jisho.txt', 'r', encoding='utf-8') as f: line = f.readline() with open('jukugo.txt', 'w', encoding='utf-8') as f2: while line: m = re.search(r'【[一-龥]{2,}】', line) if(m): print(line.strip()[1:-1], file=f2) line = f.readline()
流れとしてはjisho.txt開いて、1行ずつ読んで、【(2文字以上の漢字)】にマッチする文字列取り出して、【】を削って、ファイル出力って感じです。
「[一-龥]{2,}」で漢字のマッチを調べてるつもりなんですがあんまり自信がありません。
部首の判定
特定の漢字とそのパーツ(部首とか偏旁)を対応させたjsonファイルを公開してる素敵な方がいらっしゃったのでjsonファイルを借りました。
この中の「kanji2radical.json」を使います。このjsonファイルは部首だけじゃなくて偏旁のデータもあって「別に部首に限らず偏旁共通でもよくね?」と思ったわけです。「推進」とか部首共通してないけどそれっぽさあるでしょ。なのでタイトルは嘘です。正しくは「部首または偏旁共通の熟語を見つけるプログラム」になります。
で、書いたのが
import json with open('kanji2radical.json', 'r', encoding="utf-8") as f: df = json.load(f) with open('jukugo.txt', 'r', encoding='utf-8') as f: ok_jukugo = set() err_set = set() line = f.readline() while line: char_line = list(line) intersection = set() flag = 1 for c in char_line: if str(c) == '\n': if any(intersection): ok_jukugo.add(line.strip()) break try: tmp = set(df[str(c)]) if flag: flag = 0 intersection = tmp else: intersection = intersection & set(tmp) except KeyError: err_set.add(str(c)) if flag: flag = 0 intersection = set(str(c)) else: intersection = intersection & set(str(c)) line = f.readline() with open('same_bushu.txt', mode='w', encoding='utf-8') as f: for s in ok_jukugo: print(s, file=f) with open('error_kanji_list.txt', mode='w', encoding='utf-8') as f: print(err_set, file=f)
一応説明します。
import json with open('kanji2radical.json', 'r', encoding="utf-8") as f: df = json.load(f)
先のjsonファイルを読み込んでjsonのデータを辞書としてdfに格納。dfのKeyは漢字1文字、Valueは部首、偏旁のリスト。
with open('jukugo.txt', 'r', encoding='utf-8') as f: ok_jukugo = set() err_set = set() line = f.readline()
熟語一覧のファイルを読み込み、所望の熟語をいれるok_jukugoとKeyが見つからないときにその文字を覚えておくerr_setをset型で宣言。なぜset型だって?同じ漢字の組で読み方が違うものが複数回出てくるからです(相殺:そうさい、そうさつ)。
while line: char_line = list(line) intersection = set() flag = 1 for c in char_line: if str(c) == '\n': if any(intersection): ok_jukugo.add(line.strip()) break try: tmp = set(df[str(c)]) if flag: flag = 0 intersection = tmp else: intersection = intersection & set(tmp) except KeyError: err_set.add(str(c)) if flag: flag = 0 intersection = set(str(c)) else: intersection = intersection & set(str(c)) line = f.readline()
lineに熟語+改行コードがある状態です。list(line)で1文字ずつ分解して見ていきます。
部首偏旁共通か否かはset集合の積集合で判定します。分解した文字それぞれの部首偏旁の積集合が空集合でなければ「部首偏旁共通の熟語」として判定します。
KeyErrorは発生する場合、それは大体「これ以上分解できない漢字」というものです。例を挙げると「米」。この場合はその漢字自体を「部首偏旁」として積集合の対象とします。こうすることで「米粉」が判定を通ります。
積集合の計算をするときにintersectionの初期値が空集合だと絶対空集合になっちゃうのでflagで一回目の演算を分岐させてます。もっと賢い方法ありそうですね。
一回tmpを挟んでるのではエラーの発生箇所を一箇所に限定したかったからです。
with open('same_bushu.txt', mode='w', encoding='utf-8') as f: for s in ok_jukugo: print(s, file=f) with open('error_kanji_list.txt', mode='w', encoding='utf-8') as f: print(err_set, file=f)
あとはファイル出力するだけ。
出力はこんな感じ、見やすさのため改行をコンマにしました。
ついでにerror_kanji_list.txtにKeyErrorが発生した漢字が記録されてます。
{'彧', '長', '竹', '雨', '八', '用', 'ま', '牛', '葈', '羊', '豕', 'か', '巾', 'ち', '水', '人', '一', '父', '食', '月', '【', 'え', '入', '艮', '金', '卜', '又', '士', '耳', 'で', '小', '工', '車', '肉', 'べ', '川', '乙', '羽', '尸', '德', 'ず', '欠', '缶', '里', '身', '匕', '山', '己', '西', '鄧', '力', '目', '心', 'ろ', '木', '十', '糸', 'み', '手', '示', '黽', '黄', '皿', '米', '燾', '寸', 'ご', '靑', '夕', '咩', '日', '刀', '戸', '大', '火', '舟', '非', '臼', '氏', '皮', '土', '斤', '女', '田', '子', '臣', '生', '二', '口', '毛', '弓', '片', '菇', 'し', '面', '門'}
なんで平仮名があんねん...
一応出力したテキストファイル共有しときます。
https://drive.google.com/open?id=1t6MZBtqEQavtFDkZGo4btNsC_Idyxypw
おわりに
楽しかったです。あとINVERSUSってゲーム面白いからやってみな。