Sample Site

GAWK 水平タブをスペースに置換する

エディタの機能にこの手のものは必ずと言っていいほど入っています。また、このようなツールは linux をはじめ、unix 系の OS には付属していることが多いです。Windowsのコマンドラインツール「More」もいつの間にか、ちゃんとできる子になっていました。テキストファイルの置換のみであれば、これらの使用で事足りますが、テキスト整形やレキシカルアナライザ等のプログラム中でそれを必要とした場合、プログラムが途切れ途切れになり、ちょっと気持ち悪いので、このスクリプトを作成することにしました。

水平タブの特徴は、注目タブ直前までの日本語を含む文字列表示長さに応じて、都合よく長さが可変するところです。しかし、見た目の空白長さが変わっても、実際には「\t」は結局 1 文字でしかありません。このからくり(スクリプト参照)がわかれば、あとは文字列表示長さを求め、スペースに置換すればよいのです。が、生憎、GAWKはアルファベットの国で生まれたため、文字列表示長さを返す関数など用意されていません。そこで別ページで紹介してますが、(プログラムに組み込む前提なのでバイトモードはNG) UTF-8/Shift_JIS どちらの環境でも使えるように、手製の blength() を利用します。

連続スペースを作成する関数は、実はあります。sprintf() です。
以下のように書きます。

ret = sprintf("%*s", tab_length, "");

tab_length にタブ長さを求める式を代入すると、必要なスペースが生成されます。いろいろな方法を試しましたが、この手法は比較的高速でした。


  置換元テキストサンプル

t2s_sample.jpg

Homeへ戻る

awk_t2s.awk

1 : #. awk_t2s.awk;タブのスペース置換 (汎用/日本語対応) 2 : BEGIN { 3 : _asc_init(); #文字列表示長取得 blength() が使う 4 : } 5 : 6 : { 7 : print tab2space($0); 8 : }

BEGINセクション先頭で大域変数(ASCII辞書) _asc[] を作成します。
ここでは筆者がよく使う 4タブ(第2引数省略) に設定します。スクリプト呼び出し時の引数として変数に代入する方法もありますが、実用を考えるとトレードオフです。
各行を順にタブ/スペース変換して標準出力に書き込みます。プログラムの一部とするなら、ここで変数に代入したり、ほかの関数の引数とします。

9 : #. tab2space();タブをスペースに置換する 10 : # str ターゲット文字列(行) 11 : # tablen タブ長さ 省略 4タブ 12 : # ※temp: bug回避のために用いている 13 : function tab2space(str, tablen, tstr, pos, temp) { 14 : if (!tablen) tablen = 4; #省略時のdefault値 4タブ 15 : do { 16 : if (pos = index(str, "\t")) { 17 : temp = tstr = tstr substr(str, 1, pos - 1); 18 : tstr = tstr sprintf("%*s", tablen - blength(temp) % tablen, ""); 19 : str = substr(str, pos + 1); #既読部切り捨て 20 : } else tstr = tstr str; 21 : } while (pos) 22 : return tstr; 23 : }

処理の流れ :置換後の文字列 tstr を作成する

  • 16: 水平タブ位置を検査 (条件式 pos代入)一度は必ず実行される
  • 17: 水平タブ位置直前までの文字列を結合
  • 18: 17に適当なスペースを計算/結合

    ※17で temp を使わず blength(tstr) とすると 引数 tstr が更新されない現象が起こることがある
     17 で tstr を temp にコピーすると blength(tstr) で期待通りの動作となるが原因不明なので
     blength(temp) とした (length() でも同様 GAWK for win32/3.1.5jp 4.x.x)

  • 19: str 既読の水平タブ位置までを切り捨てて更新
  • 20: 水平タブが見つからなくなったら str残余文字列を結合
  • 21: 水平タブが見つかっている間はループ

    24 : #. _asc_init();ASCII+半角カナ辞書(Shift_JIS) 25 : function _asc_init( i, hk, ar, qt) { 26 : for (i = 0; i < 128; i++) _asc[sprintf("%c", i)] = i; 27 : hk = "。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚"; 28 : qt = split(hk, ar, ""); 29 : for (i = 1; i <= qt; i++) _asc[ar[i]] = 160 + i; #Shift_JIS 30 : _SCLP = " "; #マルチバイト文字の断片を表す文字 31 : } 32 : #. blength();文字列長さ疑似バイトを返す(辞書_asc) 33 : function blength(str, i, ret, lenb) { 34 : lenb = 0; 35 : while (ret = substr(str, ++i, 1)) 36 : (ret in _asc) ? lenb += 1 : lenb += 2; 37 : return lenb; 38 : }

    これらの関数はコチラで解説しています。

    実行結果

    t2s_exec.jpg

    UTF-8/MSYS2/GAWK5.0 t2s_exec01.jpg
    注:環境依存文字、例えば「αβγδД」は、正しく認識できない 環境(使用フォント含む)に合わせて、上記 _asc_init()に登録することで 正しく認識させることはできるが、環境依存であることを忘れずに