#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のトップページをご覧ください。