【Mac】なろう記法の自動化 2
なろう記法を自動化で「小説家になろう」のやり方でルビを振るというシェルスクリプトを書いた。
前回のスクリプトにはひとつ、重大な欠点があった。「美味しい」を「おいしい」と読ませたいのに、「美味」のふりがなが「びみ」になってしまうのだ。
これを修正するため、いろいろ考えて、入力した文章の漢字ぜんぶにルビを振る、というシェルスクリプトを作ってみた。
シェルスクリプト
苦労して作りあげたのが以下である。長くなってしまった。
あまり長い文章は処理できないと思う。
未知語の場合は最初からルビを振らない。
前回と同じく、mecab と nkf の力をおおいに借りている。
#!/bin/bash
function getKanji() {
local kan=`echo $1 | nkf --hiragana`
local moji=`echo $2 | nkf --hiragana | grep -o '.'`
local chara=(`echo $moji`)
for c in "${chara[@]}"
do
kan=`echo $kan | sed -e "s/$c//"`
done
echo $kan
}
str="$1"
wakati=`echo "$str" | mecab -O wakati`
arr=(`echo $wakati`)
for word in "${arr[@]}"
do
michigo=`echo "$word" | mecab -x "未知語" | grep '未知語'`
if test -n "$michigo"; then
arr2+=("$word")
continue
fi
if echo "$word" | grep -sqi '[a-z][0-9]'; then
arr2+=("$word")
continue
fi
yomi=`echo "$word" | mecab -O yomi | nkf --hiragana -w`
katakana=`echo "$word" | mecab -O yomi | nkf -w`
if test "$word" != "$yomi" -a "$word" != "$katakana"; then
kanji=`getKanji $word $yomi`
okuri=`getKanji $word $kanji`
if test -z $okuri; then
jukugo=`echo '|'$kanji'《'$yomi'》'`
arr2+=("$jukugo")
else
if echo $word | grep -sq "$kanji"; then
furi=`getKanji $yomi $okuri`
rubi=`echo '|'$kanji'《'$furi'》'`
furigana=`echo $word | sed -e "s/$kanji/$rubi/g"`
arr2+=("$furigana")
else #「置き引き」「女の子」「止まり木」問題
okuCha=`echo $okuri | nkf --hiragana | grep -o '.'`
okuriChara=(`echo $okuCha`)
kanAr=`echo $word | nkf --hiragana`
furiAr=$yomi
for oc in "${okuriChara[@]}"
do
kanAr=`echo $kanAr | tr "$oc" '\n'`
furiAr=`echo $furiAr | tr "$oc" '\n'`
done
kanjiAr=(`echo $kanAr`)
furiganaAr=(`echo $furiAr`)
furigana=`echo $word | nkf --hiragana`
int=0
for ht in "${kanjiAr[@]}"
do
rubi=`echo '|'$ht'《'${furiganaAr[$int]}'》'`
furigana=`echo $furigana | sed -e "s/$ht/$rubi/"`
int=`echo $((int + 1))`
done
arr2+=("$furigana")
fi
fi
else
arr2+=("$word")
fi
done
for s in "${arr2[@]}"
do
echo -n "$s"
done
echo ''
スクリプトについて
furigana.sh '文章'
というふうに使う。
$ furigana.sh '生ビールは美味しい'↩
|生《なま》ビールは|美味《おい》しい
mecab -O wakati
で分かち書きし、それを配列に入れて処理を進めている。
getKanji()
という関数は形態素のなかの漢字を取り出すためのもの。最初の引数に処理する形態素、二番目の引数に、その形態素の読みを投じる。
grep -o '.'
とやると、文章をひと文字ずつバラしてくれる。
$ echo 'おどろき' | grep -o '.'↩
お
ど
ろ
き
この「おどろき」を、原文の「驚き」とひと文字ずつ突きあわせて、sed で合致したものを消していく。この場合は「き」が合致するので「驚」だけが残る、はずだ。
漢字を取り出したら、ふたたび sed で「驚き」から「驚」を消し、「き」だけを残す。これを送り仮名と考える。今度は読みの「おどろき」から「き」を消し、「おどろ」を残す。これが振り仮名だろうと考え、「|驚《おどろ》き」という形に処理する。
ところがこの方法だと、「置き引き」や「女の子」「止まり木」などの、ひとつの形態素に、漢字がひらがなを挟んで複数ある場合はうまくいかない。
「置き引き」から漢字を取り出すと「置引」という、原文にない言葉になってしまい、sed による検索・置換が出来ないのである。
これについて対処したのがコメントから下の部分である。
以下のようにすれば、漢字と振り仮名が対応する配列が作れるはずだ。
$ echo '置き引き' | tr 'き' '\n'↩
置
引
$ echo 'おきびき' | tr 'き' '\n'↩
お
び
tr コマンドで、送り仮名にあたる文字を改行にしてみたのである。これで、「置」に「お」を、「引」に「び」という振り仮名を振れる。
このスクリプトの限界
いかにも粗雑で穴のありそうなスクリプトであり、実際にある。以下のような例文はうまくいかない。
$ furigana.sh '仕返ししてやる!'↩
|仕返《かえし》ししてやる!
振り仮名の「し」が送り仮名の「し」で消えてしまうのだ。
仕返しのほかには、「最も(もっとも)」、「大嫌い(だいきらい)」などが、このパターンに該当する。上に記したような考えかたでは解決できないかもしれない。
これを「仕返し問題」と命名して、後の自分にたくす。なにか方法を思いついたら修正するかもしれない。
追記(19.03.03)
ひとつだけ思いついた。getKanji 関数に rev を噛ませるのだ。
function getKanji() {
local kan=`echo $1 | rev | nkf --hiragana`
local moji=`echo $2 | nkf --hiragana | grep -o '.'`
local chara=(`echo $moji`)
for c in "${chara[@]}"
do
kan=`echo $kan | sed -e "s/$c//"`
done
echo $kan | rev
}
rev は文字列を逆さまにするコマンドである。
$ echo 'しかえし' | rev↩
しえかし
こうやってから頭の「し」を送り仮名の「し」で消し、ふたたび rev をかければ、「しかえ」が出力される。対症療法的だが、すくなくとも前掲した語句「仕返し」「最も」「大嫌い」は、これで正しく振り仮名を振れる。