囲まれた文字列の置換

awk ファンクラブからですが、プログラム板に立てるべき内容だと思いつつ、こうした内容のスレッドができるのは非常にうれしいです。 さて、問題は以下のような内容です。

みなさん教えて下さい。

一行毎に読み込み@<と@>で囲まれた部分を
ランダムなアルファベット10文字に置換し、
元々@<@>に囲まれた部分の文字列と置換したランダムな文字列の対応を
別ファイルに書き出し

っていうのをやりたいです。
awkとsedでできるの?

pythonで書くべき?

ずばり、awk でできます。

ここに解答が出ていて、match という単語を目にしてしまったために作った解答が以下のものです。

#! /usr/bin/gawk -f
# random_char.awk

{
    new_str = "";
    if (match($0, /@<.*@>/)) {
        for (i = 1; i <= 10; i++) {
            new_str = new_str sprintf("%c", int(rand() * 26) + 65);
        }
        print substr($0, RSTART + 2, RLENGTH - 4), new_str > output1;
        $0 = substr($0, 1, RSTART - 1) \
             "@<" new_str "@>" \
             substr($0, RSTART + RLENGTH);
    }
    print $0 > output2;
}

解答のものと比較すると文字が大文字だけしか対応していません。

テストするために以下のような random_char.in というファイルを用意します。

@<aaa@>bbb
ccc@<ddd@>
eee@<fff@>ggg
hhh@<iii@>jjj@<kkk@>lll

これに対して実行してみます。

$ gawk -v output1="output1.txt" -v output2="output2.txt" -f random_char.awk random_char.in

$ cat output1.txt
aaa GHVDPFVEMD
ddd JMXGXFQOIC
fff NOWBCQASCM
iii@>jjj@<kkk GRXTSXYJXU

$ cat output2.txt
@<GHVDPFVEMD@>bbb
ccc@<JMXGXFQOIC@>
eee@<NOWBCQASCM@>ggg
hhh@<GRXTSXYJXU@>lll

output1 が対応表で、output2 が置換後のものです。 1 行に 2 回括られたものが存在する場合には、match() 関数が最長一致で 1 つとして認識しています。 もちろん、最短一致の match() 関数のようなものを作ることも可能ですので、必要に応じて作ってみてください。

あと、海外の awker で良く使われるものは FS (フィールドセパレータ) そのものを変更してしまう方法です。

#! /usr/bin/gawk -f
# random_char_2.awk

BEGIN {
    FS = "@<|@>";
    OFS = "";
}

{
    for (i = 1; i <= NF; i++) {
        new_str = "";
        if (i % 2 == 0) {
            for (j = 1; j <= 10; j++) {
                new_str = new_str sprintf("%c", int(rand() * 26) + 65);
            }
            print $i " " new_str > output1;
            $i = "@<" new_str "@>";
        }
    }
}

{
    print $0 > output2;
}

こちらの場合には、置換対象を示すタグが必ずペアになっていることが必要になりますが、非常に簡単に記述できます。

$ gawk -v output1="output1.txt" -v output2="output2.txt" -f random_char_2.awk random_char.in

$ cat output1.txt
aaa GHVDPFVEMD
ddd JMXGXFQOIC
fff NOWBCQASCM
iii GRXTSXYJXU
kkk IFZESNYSCF

$ cat output2.txt
@<GHVDPFVEMD@>bbb
ccc@<JMXGXFQOIC@>
eee@<NOWBCQASCM@>ggg
hhh@<GRXTSXYJXU@>jjj@<IFZESNYSCF@>lll

1 行に複数個存在しても対応できます。 FS に正規表現を用いることができるのは gawk だけですが、Linux 板に立てたスレッドということで awk といえば gawk または組み込みでも Busybox の awk であることから実行可能と判断しました。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png