▽skarg

ちょっと欲しくなったので、引数をファイルに保存するプログラムを書いてみます。

○skarg 初期バージョン

5分とかからずに書き上げたサンプルプログラムがこちら。

skarg_s-00.lzh(初期バージョン)
skarg_s-00.lzh

プログラムのメインルーチンはこんな感じ。

int             main( int argc,char* argv[] )
{
    //      プログラムに渡された引数をファイルに保存する

    //      単独で実行されたときは使い方を表示

    if( argc == 1 ) {
        PrintUsage();
        return  EXIT_SUCCESS;
    }

    //      引数を書き出し

    FILE*   fp  = fopen( "skarg_s.txt","wt" );
    if( fp == NULL )
        return  EXIT_FAILURE;

    for( int i=0; i<argc; i++ )
        fprintf( fp,"%2d: %s\n",i,argv[i] );

    fclose( fp );

    //      終了

    return  EXIT_SUCCESS;
}

とりあえず、プログラムの目的はこれで十分満たされています。他にも『ソースがわかりやすい』、『実行ファイルが小さい』などの利点もあります。

これで満足しても良いんですけど・・・ ちょっと気になる点もあるので改良してみましょう。改良するポイントは以下の通り。

・ファイルの保存先を固定する
今のままでは引数を保存するファイルはカレントディレクトリに作られます。プログラムの性質からして、他のプログラムから実行されるはずですからカレントディレクトリがどこになるのかはわかりません。
この問題を回避するため、引数を保存するファイルはプログラムが置いてあるディレクトリに保存するようにします。
・保存するファイルのファイル名が固定
いまのところ、ファイル名は"skarg_s.txt"で決め打ちされています。このままだと、複数回実行された場合などで、古い保存結果は上書きされてしまいます。
この問題を回避するため、連番でどんどんファイルを増やすようにしてみます。ついでに、保存するファイルのファイル名は実行形式のプログラム名から決めるようにします。
・ファイルの書き出しがfopen()形式
これは問題でも何でもないんですけど、上の改良のついでにちょっとしたストリーム入出力を行うようにしてみます。

○skarg フルカスタマイズバージョン

初期バージョンを元に、思いつくまま気の向くまま、改良(?)を施したのがこちらのバージョンです。

skarg-30.lzh(フルカスタマイズバージョン)
skarg-30.lzh

以下、改良点をいくつか解説していきます。

○ファイルの保存先パス&ファイル名

保存先のファイル名を確定させているルーチンはAFindFile::GetOutFilename()です。具体的にはこんな感じ。

    GString         GetOutFilename( void )
    {
        //      プログラムと同じパスにファイルを書き出す

        GString     tmp = GGetExecFilename();
        GFilename   fn( tmp );
        GString     pat = fn.GetDrivePath();

        //      検索用のパターンを作成

        m_pos   = fn.m_name.GetSize();
        tmp.Format( "%s??.txt",fn.m_name );

        Search( pat,tmp,false );

        //      最大の番号+1をファイル名にする

        tmp.Format( "%s%s%02d.txt",pat,fn.m_name,m_no + 1 );

        return  tmp;
    }

実行中のプログラムのファイル名はGetModuleFileName()で取得できます。ただし、そのままGetModuleFileName()を使うのも芸がないのでGGetExecFilename()で扱いやすくしてあります。

単純にパスを切り出すだけならGGetPath()関数も用意してあるのですが、このあと、保存用のファイル名を作成するためにも使う事を考えてGFilenameクラスを使ってファイル名を詳細に分割してあります。

GFilenameクラスは与えられたパスをドライブ名・パス・名前・拡張子など詳細に分割するクラスです。ファイル名の取り扱いはまじめにやろうとするときりがないですけど、こういう便利クラスを1回作っておけばかなり楽になるかと。

保存先のフォルダ・ファイル名の主要部分が確定したところで、連番を振るための調査に入ります。これには、AFindFileクラスの親クラスであるGFindFileのファイル検索機能を使っています。

パスとパターンを指定してSearch()でそのフォルダのファイルを検索させます。パターンに一致したファイルが見つかるとOnFindFile()が呼び出されるので番号を調べて、一番大きな番号を記憶しておきます。

最終的には『パス』+『名前』+『見つかった一番大きな番号+1』+『.txt』で、保存するファイル名が確定します。

○ストリーム入出力

引数をファイルに保存する部分はこんな感じになりました。

    //      ファイルに書き込み

    GFileIO     fio;
    if( fio.Create( fn ) == false )
        return  EXIT_FAILURE;

    GTextBuffer tb;
    tb.Create( &fio );

    GString tmp;
    int     rc  = EXIT_SUCCESS;

    try {
        char*   arg = GetCommandLine();
        tmp.Format( "all: %s\r\n",arg );
        tb.WriteLine( tmp );

        for( int i=0; i<argc; i++ ) {
            tmp.Format( "%3d: %s",i,argv[i] );
            tb.WriteLine( tmp );
        }

        tb.Flush();
    }
    catch( ... ) {
        rc  = EXIT_FAILURE;
    }

    tb.Close();
    fio.Close();

GFileIOはWin32のCreateFile()系の関数を使ったファイル入出力クラスです。このままだと、テキスト形式の入出力には使いにくいので、さらに、GTextBufferを使ってバッファリング&テキスト形式に対応させてから使っています。

○クラス&関数解説

skargはmain.cppだけを見るとそれなりにあっさり出来てますが、その裏ではいくつものクラスや関数を使って面倒な事を簡単にこなしています。それぞれ、簡単な説明は以下の通り。

・デバッグ関係の関数(GDebug.hで宣言)
デバッグに便利な関数が納められています。Win32ベースで開発する人って、みんな、こういうものは自作してるんでしょうか? OutputDebugString()をラップした関数とか、マクロとか、こういうもの無しにWin32の開発ってやってられないと思うんですけど・・・
・動的配列クラス(GArray / GClassArray)
必要に応じてサイズが増減する配列です。ちなみに、自分のプログラムではGArrayが多用されます。
GArrayは基本データ型から単純な構造体向け。GClassArrayはクラスとしての取り扱いが必要な場合に使います。
・ストリーム入出力基本クラス(GStreamIO / GFileIO)
GStreamIOはストリーム処理の基本的な機能を実装したクラスです。純粋仮想関数を含むので、このままでオブジェクト化する事は出来ません。GFileIOはGStreamIOを継承したCreateFile()系のAPIを使って入出力を行うクラスです。
全体的に例外処理とエラーコードでの処理とのバランスがいまいちですが、まぁ、実用上は問題ないかなぁ・・・ やっぱり、ストリームは全部例外処理にするのが正解なのでしょうかねぇ?
・バッファクラス(GStreamBuffer / GTextBuffer)
GStreamBufferは基本的なバッファクラスです。GStreamIOが使えるところならどこでも使えます。GTextBufferはテキストに特化したバッファクラス。同じバッファクラスでも、こっちはテキスト専門です。
・その他(GString / GException / GFilename / GFindFile)
GStringはよくある文字列クラスです。GExceptionは例外処理を行うときの基本となるクラス。GFilenameはファイル名の分割を行うクラス。ファイル名関係のちょっとした関数も同じ場所に入れてあります。GFindFileはファイル検索クラスです。Win32のFindFirstFile()系の処理を使いやすくラップしたものです。

詳細な解説はしません。詳細はソースを参照という事で(o^-')b

○今後の予定

しばらくはコンソール向けのソフトを開発しそうな予感。

この中から何本か・・・ 満足するまで作る事でしょう。たぶんw

ちなみに『カレントフォルダ変更ソフト』はまじめにやるとなかなか出来ないソフトだったり。さて、どうやるんでしょう?


[GClass] Presented by See.Ku [2005/2/12]