#pragma twice

KAB-studio > プログラミング > #pragma twice > 295 Version 14.28 再帰呼び出しでフォルダを潜る

#pragma twice 295 Version 14.28 再帰呼び出しでフォルダを潜る

前のページへ 表紙・目次へ 次のページへ

 Version 14.28
再帰呼び出しでフォルダを潜る

「今回は前回の続き、ファイルの検索です」
『結構長いから途中で止まっちゃったんだよね……えっと、次は……』

        else if( _stricmp
                ( stWin32FindData.cFileName
                , p_pchFileName 
                ) == 0 )
        {
            // ファイル名が一致しました。
            ++iNum;
        }

『ファイルのチェックだよね。これは前回と同じ、と』
「あ、でも前回 _stricmp() に触れなかったよね」
『そういえばそうだよね、ってゆーか _ 付いてるじゃん、全部小文字だか
らこれってランタイムだよね。 _ 付きって使っていいんだっけ?』
「 _ が付いているランタイムは Visual C++ にしか入ってないランタイム。
だから、ちょっと汎用性ないんだよね」
『でもさ、ファイル名の一致チェックなんて簡単にできるんじゃないの?』
「ファイル名って大文字と小文字を区別しないでしょ」
『……だから?』
「だから。実は大文字小文字を区別しないランタイムってないんです」
『ええー!? なんか不便!』
「なんだけどね。 CString::CompareNoCase() っていうメンバ関数もあるか
らそっちでもいいけど」
『 CString 使うってこと? なんかそのためだけに CString 使うっていう
のも……』
「それに、 CString::CompareNoCase() の中で _stricmp() 使っているか
ら」
『同じじゃん!』
「もし _stricmp() を使わずに自作する場合には」

・大文字→小文字変換関数を作る。
・ランタイムの strcmp() で比較する。

「っていうのがいいかな」
『あー、大文字小文字を統一しちゃうわけね、先に……』
「さて、ここまでがファイル検索部分。ここまでで、指定されたフォルダの
中身は検索できました」
『ってことは、次はフォルダの中に潜るわけね』
「そういうこと。それがこの部分」

    // フォルダに対して再帰呼び出しをします。
    for( int iF1 = 0; iF1 < cFolderStrAry.GetSize(); ++iF1 )
    {
        if  (
            ( cFolderStrAry.GetAt( iF1 ) == "." ) ||
            ( cFolderStrAry.GetAt( iF1 ) == ".." )
            )
        {
            // . と .. はスキップします。
            continue;
        }

        // フォルダを連結します。
        CString cPathStr = p_pchFolderPath;
        if  (
            ( cPathStr.GetAt
                ( cPathStr.GetLength() - 1 ) != '\\' ) ||
            (
                ( cPathStr.GetAt
                    ( cPathStr.GetLength() - 1 ) == '\\' ) &&
                ( _mbsbtype
                    ( (const unsigned char *)(LPCTSTR)cPathStr
                    , cPathStr.GetLength() - 1 
                    ) == 2 )
            )
            )
        {
            // 最後に \ がなければくっつけます。
            cPathStr += "\\";
        }
        cPathStr += cFolderStrAry.GetAt( iF1 );
        // 再帰呼び出しします。
        iNum += CountMatchFile( cPathStr, p_pchFileName );
    }

「 cFolderStrAry にはみつかったフォルダを入れてあるので、その数だけ
ループして処理します」
『おー』
「次に、【.】と【..】を省きます」

        if  (
            ( cFolderStrAry.GetAt( iF1 ) == "." ) ||
            ( cFolderStrAry.GetAt( iF1 ) == ".." )
            )
        {
            // . と .. はスキップします。
            continue;
        }

『な、何これ……』
「火美ちゃんは DOS コマンドの DIR って使ったことある?」
『時々あるけど……ファイル一覧出すヤツでしょ?』
「こんな感じに出るでしょ」

2004/10/31  02:22       <DIR>          .
2004/10/31  02:22       <DIR>          ..
2005/02/07  15:04       <DIR>          Pragma_twice
1999/03/26  14:11                2,673 Plot.txt

『あ! そういえばこの時に【.】と【..】って出る! 確か、【.】って
今表示してるフォルダで、【..】はひとつ上のフォルダのことだよね』
「そう。実はこのふたつのフォルダも、 FindFirstFile() を使った検索で
取得されるんです」
『げ!』
「ま、 DIR コマンドと同じってことだね」
『でもこれをフォルダにして検索しちゃったら……永久ループ?』
「になっちゃうので、これをスキップするのを忘れないようにしてくださ
い」
『うん……これ結構怖いね……』
「まぁエラーになって落ちるだけだけど」
『? ずっと動きっぱなしじゃないの?』
「うん、その理由は最後に。その前に、フォルダの連結をします」
『フォルダの連結?』
「引数の p_pchFolderPath には、検索対象のフォルダが渡されているで
しょ。たとえば "C:\\WinNT" とか」
『うんうん』
「このフォルダのフルパスに、次に検索するフォルダ、たとえば "system" 
をくっつけて "C:\\WinNT\system" にします。それがこれ」

        // フォルダを連結します。
        CString cPathStr = p_pchFolderPath;
        if  (
            ( cPathStr.GetAt
                ( cPathStr.GetLength() - 1 ) != '\\' ) ||
            (
                ( cPathStr.GetAt
                    ( cPathStr.GetLength() - 1 ) == '\\' ) &&
                ( _mbsbtype
                    ( (const unsigned char *)(LPCTSTR)cPathStr
                    , cPathStr.GetLength() - 1 
                    ) == 2 )
            )
            )
        {
            // 最後に \ がなければくっつけます。
            cPathStr += "\\";
        }
        cPathStr += cFolderStrAry.GetAt( iF1 );

『……あれ? なんか前回と違うような……』
「ごめんなさい、前回のプログラム、間違えてました」
『げ』
「これから説明する日本語処理のところが……」
『だめねー』
「……日本語処理がなければ簡単なんだけどね。まず」

        CString cPathStr = p_pchFolderPath;

「で検索したフォルダ "C:\\WinNT" を入れて」

            cPathStr += "\\";

「で \ を最後にくっつけて "C:\\WinNT\\" にして」

        cPathStr += cFolderStrAry.GetAt( iF1 );

「でその後ろにフォルダ "system" をくっつけて」
『 "C:\\WinNT\system" にするわけねー。でも、なんかその \ をくっつけ
るとこですごく大変なことしてるんだけど……』
「二重に if してるからね。まず」

if( cPathStr.GetAt( cPathStr.GetLength() - 1 ) != '\\' )

「これは検索したフォルダの一番後ろに \ が付いてないか。もし付いてる
ところに \ を付けると \ が二重に付いちゃうから」
『そっか、後ろに付いてないときだけ \ を追加するわけね』
「でも、このチェックだけじゃダメなんです。 Version 11.04 ( No.204 ) 
を思い出して」
『んー、文字コード……あ!! 【ソ】って、 \ が混ざってるんだ!』
「【ソ】みたいに \ がトレイルバイトにある文字が一番最後にあると、 \ 
がくっついているって勘違いしちゃうんです。だから」

if( cPathStr.GetAt( cPathStr.GetLength() - 1 ) == '\\' )

「の時、つまり最後に \ のコードがある場合、」

if( _mbsbtype
    ( (const unsigned char *)(LPCTSTR)cPathStr
    , cPathStr.GetLength() - 1 
    ) == 2 )

「で、トレイルバイトなら \ とみなさずくっつける、ってしてます」
『そんなめんどくさいチェックが必要なんだね……ん? なんか変なキャス
トしてるけどこれって?』
「まず、  LPCTSTR にキャストしてるのは cPathStr の文字列ポインタを取
り出すため。 Version 5.22 ( No.087 ) 、 Version 7.09 ( No.129 ) 、 
Version 11.20 ( No.220 ) を参照」
『あー、 CString クラスの中に文字列が入ってるから、それを取り出すた
めの operator LPCTSTR を呼ぶってゆーことね』
「そういうこと。次に (const unsigned char *) にキャストしてるのは、 
_mbsbtype() の引数がそうだから。これは Version 11.05 ( No.205 ) を参
照」
『比較するときは unsigned でないとまずい、って話ね』
「というわけで、これで cPathStr に次に検索するフォルダのフルパスが入
りました。そこで、このパスで自分自身を呼び出します」

        // 再帰呼び出しします。
        iNum += CountMatchFile( cPathStr, p_pchFileName );

『あ、再帰呼び出し! Version 13.16 ( No.252 ) のクイックソートの時
にやったね』
「こうやって、再帰呼び出しすれば、ひとつ深いフォルダの中を同じように
検索できるわけです」
『はー、なるほどねー』
「で、先ほどの答。もし無限ループになっていた場合、この再帰呼び出しが
永遠に行われます」
『うん、行われるね』
「さて、再帰呼び出しの復習。この関数の中のローカル変数、たとえば」

    int iNum = 0;

「とかは、再帰呼び出しするとどうなる?」
『んー、確か新しく作られるんだよね。関数の中の変数って呼ばれて使われ
るときに作られて、それは同じ関数呼ぶんでも同じ、なんだよね』
「そういうこと。ってことは、再帰呼び出しで CountMatchFile() が 100 
段呼ばれたら、 iNum は」
『 100 個作られる……再帰呼び出しで無限ループすると、変数が無限に作
られる……メモリ足りない!』
「というエラーになるわけです。正確には【スタックオーバーフロー】って
いうエラー」
『だから永久に動き続けるわけじゃなくて、途中でエラー出て止まっちゃう
わけね』
「そういうこと」

/*
    Preview Next Story!
*/
『ん、これで一応ファイル検索はできてるわけね』
「でも検索中はなにもできない」
『というわけでマルチスレッドの出番!』
「そういうこと。というわけで次回」
< Version 14.29 別スレッドで検索! >
『につづく!』
「いよいよマルチスレッドを使った実践的な内容に!」
『つか今度は間違えないでよね』
「う”」
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。