このブログを検索

2012/12/26

ログのパース

今まではスペースでsplitして、何番目が何で、などとやっていたが、「プログラミングPerl」をざっと読んで、 いろいろ新しいことを覚えたのでそれを生かして改善してみた。
my $logfilename = './access_log';
open my $logfh,"<$logfilename" or die $!;

while(<$logfh>){

    my @dataname = qw/client_address identifier user_name time request last_status sent_bytes referer user_agent/;
    my @logdata = $_ =~ /(.*) (.*) (.*) \[(.*)\] "(.*)" (\d+) (\d+) "(.*)" "(.*)"/;
    my %logdata;

    my $i=0;
    for (@logdata){
        $logdata{$dataname[$i]} = $_;
        $i++;
    }


    foreach my $key(keys %logdata){
        print "$key : $logdata{$key}";
    }
}
close($logfh);
これはcgiのソースの一部である。 キモはパースした内容をハッシュに格納しているところだ。 今までは配列(リスト)に格納して欲しい内容の位置にアクセスしていた。 それを、いったんデータの名前だけをリストで定義し、それをキーとしてパースした結果をハッシュに格納した。 こうすれば、欲しいデータを名前で探せる。 foreachするときに、配列のインデックスが知りたかったのだがわからなかった。 「$.」ではなかった。 なのでインデックスを変数としてインクリメントした。 ここはダサい。 apacheのログパースをするモジュールなんてのもあるが、これくらいは自分でやりたい。 正規表現も、検索すればちゃんとしたものが見つかるが、これくらいは自分で書きたい。

 1.まずフォーマットを書く。
.* .* [.*]

2.取り出したいところをカッコで囲む
(.*) (.*) [(.*)]

3.エスケープを必要なところに入れる。
(.*) (.*) \[(.*)\]

4.動かして、うまくいかなかったら直す。

 「プログラミングPerl」は、Perlの聖典であるかのように、かつ、難しいと言われているが、夕べなんとなく開いて読んでいたらおもしろくて、それほど難解なこともなく、すーっと読んでしまった。

関数やライブラリモジュールの一覧解説があるので分量が多いが、それを除けばそれほどでもない。 私のもっているのは古くて、2nd Edition、1997年発行のものである。

 「初めてのPerl」「続・初めてのPerl」ももっていてだいたい読んだが、「プログラミングPerl」も早いうちに読むべきだと思う。 文中に紹介されているサンプルを知っているといないとでも大違いだ。

私はPerlを書いていてゴチャゴチャしてくると、「こんな難しくなるはずはない」と、それらのサンプルを思い出しながら、思う。