初歩のシェルスクリプトで遊ぶ[ぬかみそフォントの制作サポート(15)]
文字種類管理シェルスクリプトで、文字コードのリストを「1」「0」のリストに変換する
簡易な文字種類を数えるシェルスクリプト『暖簾』について。
昨日は普通のテキストファイルを文字コードのリストに変換するスクリプトについて書いた。今日は文字コードのリストから、「1」「0」のリストに書き換えるスクリプトを組みます。
味噌
21619 0 22092 0
0 1 1 0
み噌󠄀
12415 0 22092 917760
1 0 0 1
Code_total
12415 0 21619 0 22092 0 22092 917760
12415 0 307f 0 み 21619 0 5473 0 味 22092 0 564c 0 噌 22092 917760 564c e0100 噌󠄀
「味噌」「み噌󠄀」の例です。
すべての文字を含んだ「Code_total」を基準にして、もしテキストファイルに文字が含まれれば、その行を「1」に。含まれないなら「0」にします。
たとえば、while readでループ処理するなら、
- 「味噌󠄀」または「み噌󠄀」から作ったコードリストファイルを、シェル変数配列(たとえば位置パラメータにsetする)に入れる。
- while readループに、「Code_total」を標準入力する。入力1行につき、出力に「1」または「0」を出す。
- もしCode_totalの文字コードが、シェル配列変数の1番(最も小さい数字)と同じならば、「1」を出力。配列をshiftする。次の文字コードを1番にずらす。
- もしCode_totalが配列1番より小さいなら、「0」を出力。配列はそのまま弄らない。
- 次のループへ
joinとrev
コメント多いですが、スクリプトは短いです。
#!/bin/sh #set -x # ========================================================================================= # ■ 「暖簾」ビットリスト作成 基準文字ファイルと比較してビットリストファイルを作成する # # # stdin 解析対象テキストのコードリストファイル。 # ${1} 行番号とunicodeの対応の基準となるコードリストファイルで、 # 解析対象テキストの文字をすべて含む。 # # どちらも、このスクリプトで使うには、ソートしておく必要はない。 # # # ▼ 処理概略 # # ・入力受け取りと加工 # # stdin --> tr \t _ 空けて末尾に1 -> 一時ファイルに記録 # [基底数字 VS数字] -> [%07基底数字%07VS数字 \t 1] -----> ${targetCode} # # ${1} tr \t _ 空けて末尾に0 # [基底数字 VS数字] -> [%07基底数字%07VS数字 \t 0] # # ・joinを実行 # 結合key 「基底文字_VS数字」 # -a 1 基準コードリストを残す。 # # 17418_0 0 # 17419_0 0 1 # 17419_917760 0 1 # 17421_0 0 # 17422_0 0 1 # 90_0 0 1 # # 全ての行には2列目に「0」がある。 # もしstdinの「基底文字+VS数字」に共通キーがあれば、3列目に「1」が付加される。 # 行末の数字が「1」ならば、stdinに文字が存在した。 # 同様に「0」ならば、文字は存在しなかった。 # 何らかのコマンドで以上を判定し、「0」「1」を出力する。 # # ▼ 「_」で文字列結合する理由 # ・入力時はsortが自然数かバージョンのため、joinできない。 # ・基底文字と異体字セレクタの両方をキーにしなければならない。 # ・ゼロ埋めするとsortを回避できるが、桁数を間違えるとまずい。 # ・「_」ならtrで簡単にタブに戻せる。 # ・sortの-k指定があれば、末尾の「0」「1」を無視して自然数ソートができる、はず。 # # ========================================================================================= # standard # 90 0 # 17418 0 # 17419 0 # 17419 917760 # 17421 0 # 17422 0 # target # 90 0 # 17419 0 # 17419 917760 # 17422 0 joinPrepSh(){ ( cut -f 1-2 | tr '\t' '_' | sort | sed -e "s/$/ ${1}/" ) } if ! file "${1}" ; then exit 1 elif test "`file -b -i "${1}" `" != 'text/plain; charset=us-ascii' ; then exit 1 else standardCode="${1}" fi 1>&2 # stdin受け取り targetCode=`mktemp` joinPrepSh '1' 1> "${targetCode}" if test -z "`cat -- "${targetCode}"`" then exit 1 fi 1>&2 #cat -- "${targetCode}" && exit #【debug】 # 17419_0 1 # 17419_917760 1 # 17422_0 1 # 90_0 1 #joinPrepSh '0' 0< "${standardCode}" && exit #【debug】 # 17418_0 0 # 17419_0 0 # 17419_917760 0 # 17421_0 0 # 17422_0 0 # 90_0 0 # 結合できなかったtargetCodeを抽出する # targetCodeが全て基準コードに含まれているならば、無出力で単語数は0 # 新しい文字が混じっているならば、単語数は2以上 if test `joinPrepSh '0' 0< "${standardCode}" | join -v 2 -- - "${targetCode}" | wc -w ` -eq 0 ; then true else echo '解析対象に未登録の文字が存在します。' 1>&2 rm -- "${targetCode}" exit 1 fi # ---- join ------------ # 17418_0 0 # 17419_0 0 1 # 17419_917760 0 1 # 17421_0 0 # 17422_0 0 1 # 90_0 0 1 # ---- sort ------------ # 90 0 0 1 # 17418 0 0 # 17419 0 0 1 # 17419 917760 0 1 # 17421 0 0 # 17422 0 0 1 # joinをタブ区切りにしておくこと HT="`printf '\t'`" joinPrepSh '0' 0< "${standardCode}" | join -a 1 -t "${HT}" -- - "${targetCode}" | tr '_' '\t' | sort -k 1n,1 -k 2n,2 | rev | cut -f 1 rm -- "${targetCode}" exit 0
- コード番号リストの、水平タブをアンダースコアに入れ替える。「基底文字_異体字セレクタ」に、繋げる。
- sort
- さらに水平タブ区切りを入れてから、行の末尾に「0」または「1」を追記する。基準文字コードリストには「0」を。解析対象のコードリストには「1」を。
- ふたつの加工したコードリストを、joinコマンドで繋げる。「-a」オプションで基準文字コードリスト側を残す。
- 解析対象のコードリストには存在しない文字の行は、行末が「(タブ)0」になる。
- 解析対象に存在するコードの行は、行末は「(タブ)0(タブ)1」になる。
- 付加したアンダースコアを除去し、基底文字と異体字セレクタの列で自然数ソート。
- 行の末尾の「0」「1」のみを抜き出すと、「0」「1」のみの、ビットのリストになる。抜き出しにはrevとcutを使う。
というようにすると、シェルでのループ操作や条件判定を書かなくても、既存のコマンドの組み合わせで処理できます。
$ cat ./Code_total.txt 12415 0 21619 0 22092 0 22092 917760 $ cat ./miso.txt 12415 0 22092 917760 $ sed 's/\t/_/;s/$/\t0/' 0< Code_total.txt | join -a 1 -- - <( sed 's/\t/_/;s/$/\t1/' 0< miso.txt ) | rev | cut -d ' ' -f 1 1 0 0 1