awkパズル:サンドイッチ問題
今日仕事で作ったプログラムがちょっと面白かったのでメモ。業務に関係する用語を別の言葉に置き換えると次のような処理です。
問題
ファイル1のような形式のファイルをファイル2の形式に変換する。つまり、最初と最後以外のパンをすべて肉に置換する。ただしパンの行数は不定。また、ファイル中にはパン以外の任意の文字列の行が含まれる。同形式のファイルが大量にあるのでawk(とシェル)を使って一括処理したい。
ファイル1(変換前)
: パン パン レタス パン トマト パン パン : :
ファイル2(変換後)
: パン 肉 レタス 肉 トマト 肉 パン : :
解答例
#!/usr/bin/nawk -f /^パン$/ { cnt++ } END { while (getline s < FILENAME) { if (s ~ /^パン$/) { pan++ if ((pan > 1) && (pan < cnt)) sub(/^パン$/, "肉", s) } print s } }
これを以下のようなバッチファイルで実行する。
for %%i in (*.txt) do gawk -f sandwich.awk %%~nxi > new\%%~nxi
Linuxならこうかな。
#!/bin/sh for i in *.txt; do nawk -f sandwich.awk $i > new/$i; done
難しいのは最後のパンをどうやって認識するか、というところ。
自分の場合は結局1回ファイルを走査してすべてのパンの数を数えてから、再度getlineでファイルを開きなおして最初と最後のパン以外を変換するようにしました。やり方はいろいろありますが、15分くらいでさくっと作る使い捨てプログラムの場合どう書くのが良いと思いますか?
※ちなみに、実行する環境は素の状態に近いWindowsマシンでした。そのためgawk.exe1個くらいなら入れられるけど、PerlやRubyをインストールするのはNGという事情があったり。