初歩のシェルスクリプトで遊ぶ[ぬかみそフォントの制作サポート(11)]
HTMLの数値文字参照を文字に変えたり整えたり
cat character.html | nkf --numchar-input --no-best-fit-chars -x
数値参照を文字に変えるなら、nkfがいいのは分かるのだけれど。
確かnkfはUbuntuのインストールディスクに入ってなかったような、入ってたような。まぁ理由付けは何でもいいんですが、文字に変えたいっていうか、10進数で書いていた数値参照を16進数に変えたいな、と。
珍しく、dashよりbashが圧倒的に速い。大抵は、なんだかんだでdashのほうが速いんだけど、今回は難しいかなぁ。
dashのスクリプト、この方法だと、コードを追加して複雑にして、高速化しても、5倍くらいにしかできませんでした。今回は、遅いのを受け入れたほうが良い気がする。
#!/bin/sh # ================================================================================== # ■ HTML数値参照を文字に変換_dash # # ・ファイル末端の改行が無いファイルにも改行が付加される # # ▼ ニセの数値参照に注意 # # 「数値参照」「ニセ数値参照」「数値参照」 # 「行頭」「ニセ数値参照」「行末」 # # İf; 16進数指定の「x」が抜けて、10進数指定で16進数になっている例 # ̄g; 「f」のつもりが「g」を叩いて、16進数の範囲を超える例 # # sedの正規表現はニセ数値参照を排除できるが、 # caseでは数値参照に挟まれたタイプミス数値参照や、行に一つだけの偽物は排除できない。 # 1行に数値参照ひとつだけ、に整形するのが理想だが、それは困難。 # printfで16進数に変換できるか試して、printfがエラーなら無変換で出力する。 # # # # # ================================================================================== # 16進数8桁を文字に変換する hexToCharSh(){ ( B3="${1%??????}" B2="${1#??}" B2="${B2%????}" B1="${1#????}" B1="${B1%??}" B0="${1#??????}" B3=`printf '%03o' 0x${B3}` B2=`printf '%03o' 0x${B2}` B1=`printf '%03o' 0x${B1}` B0=`printf '%03o' 0x${B0}` # utf8文字で出力する。改行は付加されない。 printf '\'${B3}'\'${B2}'\'${B1}'\'${B0} | iconv -f utf32be -t utf8 # サブシェルの括弧は外すことができる。多少は高速化する。 # 括弧を外すときはexitを削除する。 exit 0 ) } while IFS='' read -r Line || test -n "${Line}" ; do # 数値参照の前後に改行を挿入する printf '%s\n' "${Line}" | sed -n -e '#n # 数値参照が存在するであろう行を通す(省略可能) /&#.[0123456789abcdefABCDEF]*;/{ # 10進数は、8進数扱いされないように先頭ゼロを削除する s/\(&#\)0*\([0123456789]*;\)/\1\2/g # 改行付加 s/&#[0123456789]*;/\n&\n/g s/&#[xX][0123456789abcdefABCDEF]*;/\n&\n/g } p ' | while IFS='' read -r linePart ; do # ---------------------------------------------------------------- # 行内分割処理 # ---------------------------------------------------------------- case ${linePart} in # ------------------------------- ( '&#'[xX]*';' ) # 16進数数値参照 # ------------------------------- linePartNum="${linePart#???}" linePartNum="${linePartNum%?}" # ニセ数値参照対策 # もともと「(行頭)&#??????;(行末)」だった文字列 if linePartNum=`printf '%08x' "0x${linePartNum}" 2>/dev/null` ; then hexToCharSh ${linePartNum} else printf '%s' "${linePart}" fi ;; # ------------------------------- ( '&#'*';' ) # 10進数数値参照 # ------------------------------- linePartNum="${linePart#??}" linePartNum="${linePartNum%?}" if linePartNum=`printf '%08x' "${linePartNum}" 2>/dev/null` ; then hexToCharSh ${linePartNum} else printf '%s' "${linePart}" fi ;; # ------------------------------- ( * ) # 数値参照以外 # ------------------------------- # 改行せずに文字だけを出力する printf '%s' "${linePart}" ;; esac done # ------------------------------- # 行末改行 # ------------------------------- echo done exit 0
#!/bin/bash # ================================================================================== # ■ HTML数値参照を文字に変換_bash専用 # # # # # # ================================================================================== # 数値参照の前後に改行ではなくnull(\o000)を挿入する sed -n -e '#n /&#.[0123456789abcdefABCDEF]*;/{ s/\(&#\)0*\([0123456789]*;\)/\1\2/g s/&#[0123456789]*;/\o000&\o000/g s/&#[xX][0123456789abcdefABCDEF]*;/\o000&\o000/g } p ' | { # ---------------------------------------------------------------- # 行内分割処理 # ---------------------------------------------------------------- # nullでreadに読み込む文字列を分割。 # シェル変数 nullSepPart に、元テキストの改行を含めて読み込む。 # 数値参照のみ独立行に分離する。 # readで最終行を読み込むための「test -n」を付加 while IFS='' read -r -d $'\000' nullSepPart || test -n "${nullSepPart}" ; do case ${nullSepPart} in # ------------------------------- ( '&#'[xX]*';' ) # 16進数数値参照 # ------------------------------- nullSepPartNum="${nullSepPart#???}" nullSepPartNum="${nullSepPartNum%?}" if printf -v nullSepPartNum '%x' "0x${nullSepPartNum}" 2>/dev/null ; then printf '\U'"${nullSepPartNum}" else printf '%s' "${nullSepPart}" fi ;; # ------------------------------- ( '&#'*';' ) # 10進数数値参照 # ------------------------------- nullSepPartNum="${nullSepPart#??}" nullSepPartNum="${nullSepPartNum%?}" if printf -v nullSepPartNum '%x' "${nullSepPartNum}" 2>/dev/null ; then printf '\U'"${nullSepPartNum}" else printf '%s' "${nullSepPart}" fi ;; # ------------------------------- ( * ) # 数値参照以外 # ------------------------------- # 改行せずに文字だけを出力する printf '%s' "${nullSepPart}" ;; esac done }
#!/bin/sh # ================================================================================== # ■ HTML数値参照を16進数小文字に統一_dash # # ・ファイル末端の改行が無いファイルにも改行が付加される # # # # ================================================================================== normarizeDecSh(){ ( # ${1} 入力10進数 if test "${1}" -ge 0 && test "${1}" -le 255 ; then # ff printf '&#x%02x;' "${1}" elif test "${1}" -ge 256 && test "${1}" -le 65535 ; then # ffff printf '&#x%04x;' "${1}" elif test "${1}" -ge 65535 && test "${1}" -le 1114111 ; then # 10ffff printf '&#x%06x;' "${1}" else exit 1 fi ) } # ---------------------------------------------------------------------- # 行単位処理 # ---------------------------------------------------------------------- while IFS='' read -r Line || test -n "${Line}" ; do # 数値参照の前後に改行を挿入する printf '%s\n' "${Line}" | sed -n -e '#n # 数値参照が存在するであろう行を通す(省略可能) /&#.[0123456789abcdefABCDEF]*;/{ # 10進数は、8進数扱いされないように先頭ゼロを削除する s/\(&#\)0*\([0123456789]*;\)/\1\2/g # 改行付加 s/&#[0123456789]*;/\n&\n/g s/&#[xX][0123456789abcdefABCDEF]*;/\n&\n/g } p ' | while IFS='' read -r linePart ; do # ---------------------------------------------------------------- # 行内分割処理 # ---------------------------------------------------------------- case ${linePart} in # ------------------------------- ( '&#'[xX]*';' ) # 16進数数値参照 # ------------------------------- linePartNum="${linePart#???}" linePartNum="${linePartNum%?}" # ニセ数値参照対策 # もともと「(行頭)&#??????;(行末)」だった文字列 if linePartNum=`printf '%d' "0x${linePartNum}" 2>/dev/null` ; then #printf '&#x%s;' "${linePartNum}" normarizeDecSh "${linePartNum}" else printf '%s' "${linePart}" fi ;; # ------------------------------- ( '&#'*';' ) # 10進数数値参照 # ------------------------------- linePartNum="${linePart#??}" linePartNum="${linePartNum%?}" if linePartNum=`printf '%d' "${linePartNum}" 2>/dev/null` ; then #printf '&#x%s;' "${linePartNum}" normarizeDecSh "${linePartNum}" else printf '%s' "${linePart}" fi ;; # ------------------------------- ( * ) # 数値参照以外 # ------------------------------- # 改行せずに文字だけを出力する printf '%s' "${linePart}" ;; esac done # ------------------------------- # 行末改行 # ------------------------------- echo done exit 0