【Webプログラミング - Code.009】CGI(Perl)ファイル処理 その2:「ファイルへ書き込む」

   Code.009                                                 2002年12月9日発行
■━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━■
                           【 Webプログラミング 】

                       〜 猫的プログラマーとその軌跡 〜
■━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━■

              ▼毎週月曜日に配信しています。
              ▼等幅フォントでご覧いただくとキレイに見えます。
              ▼登録・解除はこちらから可能です。
                < http://www.ichikoro.com/webp/ >
                  ※ぜひお友達にもご紹介ください(^^)/

こんにちは、編集者の勝部です。
何のアナウンスもしていませんでしたが、

    ☆開始から一周年突破!
    ☆総配信回数が50回突破!
    ☆配信数が2200部突破!

おめでた続きだったのをすっかり忘れていました(^^;
イベントかフェアでもやりたい所ですが、いい案ある方います?
思いついたらコチラまでぜひぜひご連絡を。

    →景気よくBBSで
      http://www.ichikoro.com/webp/bbs/

    →こっそりとメール
      mm-webp@ichikoro.com

勢い的にはSegwayの一つでもプレゼントしたいのですが、
そんなお金があったら独り占めしてます(笑)

    ・Segway
      http://www.segway.com/
        昔「GINGER」と言う名前で騒がれたアイツ。
        純粋に「欲しい!」と思わせる一品ですね。
        日本の公道を走れるかどうかはギモンですが(^^;<原付でいけるか??
        サーキット走行はイヤだぞ。

        何はともあれ、まずはビデオをご覧アレ。
            http://www.segway.com/connect/multimedia.html


というわけで、今週も行ってみましょう〜。


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 CGI(Perl)ファイル処理 その2:「ファイルへの書き込み」
──────────────────────────────────────
突然ですが、前回お届けしましたY氏のストーリーは都合により休載させていただき
たいと思います(^^; (ミッション性は残ります)

    ▼前回のメルマガはこちら
        ・CGI(Perl)ファイル処理 その1:「ファイルを読む」
          http://www.ichikoro.com/webp/bk/00053/


今回の目的はファイルに書き込みをすることです。



    Mission : アクセスログを記録するCGIを作成せよ
    ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
        ○問題
            Webサイト全体がどれくらい見られているのか確認したい。
            各ページに小さな透過GIFを呼び出す、下記のようなタグを
            埋め込み、

                <IMG src="/cgi-bin/log.cgi" width="1" height="1">

            上記log.cgiが画像を出力するとともに、ログを記録せよ。
            またファイルは日毎に分けること。

        ○データ
            以下のログを記録する
            各項目の区切りはタブ(\t)、行の最後は改行(\n)すること

              ・YYYY/MM/DD
              ・hh:mm:ss
              ・HTTP_REFERER  ※参照元ページ(呼び出したページ)。
                                環境に依存しますので、場合によっては
                                呼び出されたページのさらに一個前の場
                                合があります。

───────
    ソース
───────
#!/usr/bin/perl

;#
;#ログ記録CGI
;#

#----------------------------------------------------------------------#
#                            モジュール                                #
#----------------------------------------------------------------------#
use strict;


#----------------------------------------------------------------------#
#                           グローバル変数                             #
#----------------------------------------------------------------------#
package G;
BEGIN{
    #■画像ファイル
    $G::IMG_FILE = "1pix.gif";
}


#======================================================================#
#                           メインルーチン                             #
#======================================================================#
package main;
{
    my $t = time();
    my $date = util::get_date($t);        #現在の日付取得(YYYY/MM/DD)
    my $time = util::get_time($t);        #現在の時間取得(hh:mm:ss)
    my $file;
    my $str;

    #-----------------------------------#
    #            ファイル名作成         #
    #-----------------------------------#
    $file  =  $date;         # $file = YYYY/MM/DD
    $file  =~ s/\///g;       # $file = YYYYMMDD
    $file .=  ".dat";        # $file = YYYYMMDD.dat


    #-----------------------------------#
    #             文字列作成            #
    #-----------------------------------#
    $str = join("\t"
                        , $date                   # YYYY/MM/DD
                        , $time                   # hh:mm:ss
                        , $ENV{'HTTP_REFERER'}    # このCGIを呼び出したページ
    );

    $str .= "\n";    #最後に改行


    #-----------------------------------#
    #              書き込む             #
    #-----------------------------------#
    open(DAT, ">>$file") or die("Can not open file : $file ($!)");
    print DAT $str;
    close(DAT);


    #-----------------------------------#
    #              画像表示             #
    #-----------------------------------#
    $|=1;
    print "Content-type: image/gif\n\n";
    open(IMG, $G::IMG_FILE) or die("Can not open file : $file ($!)");
    binmode(IMG);               #バイナリだと指定(UNIXで使用する場合は必要ない)
    print while(<IMG>);
    close(IMG);


    #-----------------------------------#
    #              正常終了             #
    #-----------------------------------#
    exit(0);
}


package util;

#--------------------------------------------------------------#
#■YYYY/MM/DDを返す
#    内容:指定された時間を、「YYYY/MM/DD」のフォーマットにした
#          文字列を返却する。
#
#    引数:(1)timeの戻り値:スカラー
#  戻り値:YYYY/MM/DD:スカラー
#--------------------------------------------------------------#
sub get_date{
    my $time = shift;
    my ( $yyyy, $mm, $dd ) = (localtime($time))[5,4,3];

    $yyyy += 1900;
    $mm   += 1 ;

    return( sprintf("%4d/%02d/%02d", $yyyy, $mm, $dd) );
}


#--------------------------------------------------------------#
#■hou:min:secを返す
#    内容:指定された時間を、「hh:mm:ss」のフォーマットにした
#          文字列を返却する。
#
#    引数:(1)timeの戻り値:スカラー
#  戻り値:hh:mm:ss:スカラー
#--------------------------------------------------------------#
sub get_time{
    my $time = shift;
    my ( $hou, $min, $sec ) = (localtime($time))[2,1,0];

    return( sprintf("%02d:%02d:%02d", $hou, $min, $sec) );
}

__END__


───────
   実行方法
───────
テキストエディタ(メモ帳やSimpleText)などで上記のソース(プログラム)を
保存してください。適当な名前(xxxx.cgi)でOKです。

CGIの詳しい実行方法については、Code.001をご参照ください。
http://www.ichikoro.com/webp/bk/00046/

  ★重要!
    このCGIを使っていると、度々ファイルがぶっ壊れます(^^;
    ある処理をすっ飛ばしているせいです。
    詳しくは後述。


───────
    解  説
───────
今回はCGIではなく、完全にPerlのお話なんですけどね(^^;


○ファイルを開く際のモード
    open関数でファイルを開く際に、ファイル名の前に以下のような
    文字列を追加すると、特別な意味を持つようになります。

        ・読み込みモード
            open(DAT, "file.dat");
            open(DAT, "<file.dat");
                ファイル名だけの指定の場合またファイル名の前に "<"を
                つけた場合、指定ファイルのデータを読むことが出来ます。

        ・新規書き込みモード
            open(DAT, ">file.dat");
                ファイル名の前に ">" をつけた場合、
                ファイルを真っ白な状態で開き書き込みを
                行うことができます。
                指定したファイルがない場合は、新しく作成されます。

        ・追加書き込みモード
            open(DAT, ">>file.dat");
                ファイル名の前に ">>" をつけた場合、
                ファイルの一番最後に書き込みを追加して行うことができます。
                指定したファイルがない場合は、新しく作成されます。

        ・読み書きモード
            open(DAT, "+<file.dat");
                その名の通り、読み込みと書き込みが、同時にできます。


○書き込む方法
    それでは、ファイルに実際に書き込むにはどうしたらよいでしょうか?
    上記のソースで言うなら以下の書き込みを行っている部分がそれです。

>    #-----------------------------------#
>    #              書き込む             #
>    #-----------------------------------#
>    open(DAT, ">>$file") or die("Can not open file : $file ($!)");
>    print DAT $str;
>    close(DAT);

    ファイルを追加書き込みモードで開いた後、

>    print DAT $str;

    と、単純にprint関数を用いています。
    print関数は、出力先を指定しない状態であれば標準出力と呼ばれる
    場所に出力しますが、上記のように

        print [ファイルハンドル] [実際の値];

    といった具合に指定することができるのです。


○テキストとバイナリー
    WindowsとMac、UNIXでは改行を表す文字コードが異なります。
    この改行コードの問題が、ここでも浮上してきます。

    問題になるのはWindowsだけなのですが(^^;
    例えば、UNIXの改行コードのファイルを開き

        open(UNIX, "<FromUNIX.txt");
        open(WIN,  ">ToWindows.txt");

        while(<UNIX>){
            chomp;
            print WIN "$_\n";
        }

        close(UNIX);
        close(WIN);

    何てソースを実行すると、ToWindows.txtの改行コードは
    Windowsの物に自動的に変換されます。これは、Windows上で
    動作するPerlが自動的にこのような処理を行っているためです。

    これが問題になるのは、画像などのバイナリデータの場合です。
    イメージ的には、

        「GIF画像をFTPする時、うっかりアスキーモードで上げてしまった」

    と同じ状態が起こってしまいます。
    たまたま改行コードと同じ値のだったデータが、意図せず変換
    されてしまうわけです。

    そこで、画像などのバイナリデータを扱う場合で尚且つWindowsを使用し
    ている場合は、

>     open(IMG, $G::IMG_FILE) or die("Can not open file : $file ($!)");
>     binmode(IMG);               #バイナリだと指定(UNIXで使用する場合は必要ない)

    このようにopenした後、「このファイルはバイナリだ」と指定する
    必要があるのです。


───────
   次回予告
───────
このCGIを使っていると、しょっちゅうファイルがぶっ壊れます。
なぜならWebは次から次へとやってくる人が、順番通りにアクセスして
くれない、つまり同時に複数の人がアクセスしてしまうからです。

有名サイトになると、同時に数十人がアクセスするなんてことも珍し
くありません。

そこで、次回は

    「書き込んでいるうちは他の人は書き込まないでね」

という処理を入れてみたいと思います。


───────
   配信予定
───────
ファイル処理編の配信予定一覧です。

    ・CGI(Perl)ファイル処理 その1:「ファイルを読む」
      http://www.ichikoro.com/webp/bk/00053/

    ・CGI(Perl)ファイル処理 その2:「ファイルへ書き込む」<<今回
    ・CGI(Perl)ファイル処理 その3:「ファイルロック」
    ・CGI(Perl)ファイル処理 その4:「ディレクトリ操作」


都合により追加・変更・中止になる可能性があります。
取り上げて欲しいテーマやご意見・ご要望はぜひ以下まで
およせください。

    ・BBS
      http://www.ichikoro.com/webp/bbs/

    ・メール
      mm-webp@ichikoro.com

今ならまだ遅くない!


───────
  分からない
───────
いまいちよく分からない場合は、以下へれっつらごー。

  ・サポートBBS
    このメルマガ専用のサポート掲示板です。
    疑問などがあったら、お気軽に書き込んでやってください(^^)/
      http://www.ichikoro.com/webp/bbs/

  ・CGIプログラミングML
    CGIなどWebに関する話題を繰り広げるメーリングリスト。
    このメルマガとは関係ありませんので発言時は注意を。
      http://www.ichikoro.com/cgi/ml/


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                           編    集    後    記
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

先週、一日会社を抜け出して「インターネットワールド」へ行ってきました。
今回のお目当ては「Webサービス」。セミナーやパネルディスカッションも充実して
おり(5本ほど受けました(^^;)有意義な時間を過ごせました。

Webサービス、聞けば聞くほど面白い技術ですね!
最初に利用するのは企業が中心だと思いますが、UDDIをRPGに応用すれば数兆通りの、
しかも世界中の人が考えた奇想天外なシナリオが実現できるんじゃない?何て、ビジ
ネス以外の利用法を考えるのもまた楽しいですね。とと、ゲーム会社さんにとっては
ビジネスですな、失礼しました。というわけでスクウェア・エニックスさん、やりま
せん?

しかし、この手のイベント系は縮小の一手をたどるのでしょうか?
規模や入場数ともにどんどん縮小しているような。
さらに来年日本で行われる予定だったMacWorldExpo(日本)は中止だそうです。
代替えイベントがあるかも、とのことですが淋しい限りです。

むー。
こう熱くなれるようなイベントは無いのか、イベントは。
祭りの血が騒いでいるぞ〜(笑)

    ・インターネットワールド ASIA2002
      http://www.idg.co.jp/expo/iw/

    ・2003年のMacworld Tokyoはキャンセル
      http://www.zdnet.co.jp/news/0212/06/njbt_01.html
      http://www.zdnet.co.jp/macwire/0212/06/ne00_expotokyoj.html

また来週お会いしましょう (^-^)/~~
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

                   【 Webプログラミング Code Sample 】

                    発  行 : ichikoro.com
                発行責任者 : 勝部 麻季人
                              < katsube@ichikoro.com >
                 Webサイト : < http://www.ichikoro.com/webp/ >
            お問い合わせ先 : < mm-webp@ichikoro.com >

                            Powerd by まぐまぐ
    All Right Reserved, CopyRight(C) 2001 Webプログラミング Code Sample
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■