ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

Sun Studio 11

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

マルチスレッド開発の傾向と対策

ディレクトリの読み込み

WindowsとUnixとでfopenやfcloseはもちろん、mkdirやchdirもだいたい同じように使えるというのに(違うところも多々あるが)、なぜかディレクトリの情報の取得だけは全然違う仕様になっている。

プログラムを移植する際に、結構毎回面倒な思いをするから、ここでちょっとメモっておく。

Windowsの場合

Windowsの場合は、FindFirstFileExとFindNextFileとFindCloseというAPI関数を使用する。

詳細な情報はMicrosoft MSDN Onlineに詳しく記載されている。

とりあえず、参考のコード

#include <stdio.h>
#include <windows.h>
#include <string>

using namespace std;

void print_cnt( int cnt )
{
    int i;
    for ( i = 0; i < cnt; i++ )
        putchar( ' ' );
}

void foo( int cnt, const string dirname )
{
    WIN32_FIND_DATA fd;
    HANDLE h;

    // ハンドルを取得する
    h = FindFirstFileEx(
        ( dirname + "*" ).c_str(),
        FindExInfoStandard, &fd,
        FindExSearchNameMatch,
        NULL,
        0
    );
    if ( INVALID_HANDLE_VALUE == h ) {
        printf( "ディレクトリ %s でエラー\n", dirname.c_str() );
        return ;
    }

    do {
        if ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
            // ディレクトリの場合
            if ( strcmp( fd.cFileName, "." ) &&
                strcmp( fd.cFileName, ".." ) ) {
                // .と..は処理しない
                print_cnt( cnt );
                printf( "ディレクトリ - %s\n", fd.cFileName );
                foo( cnt + 1, dirname + fd.cFileName + "\\" );
            }
        }
        else {
            // ファイルの場合
            print_cnt( cnt );
            printf( "ファイル - %s\n", fd.cFileName );
        }
    // 次のファイルを検索する
    } while ( FindNextFile( h, &fd ) );

    // ハンドルを閉じる
    FindClose( h );
}

int main()
{
    foo( 0, ".\\" );
    return 0;
}

まぁ、ほとんど迷う余地もないわけだが。

Unixの場合

Unixの場合は、opendirとreaddir、closedirのシステムコールを使用する。また、ディレクトリか否かを判断するためにはstatを使用する。

一応、各関数の仕様はopendirreaddirclosedirstatあたりを参考にしてください。

同じくサンプルのコード。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h> 
#include <unistd.h> 
#include <dirent.h>
#include <string>

using namespace std;

void print_cnt( int cnt )
{
    int i;
    for ( i = 0; i < cnt; i++ )
        putchar( ' ' );
}


void foo( int cnt, const string dirname )
{
    DIR *pDir;
    struct dirent *pEnt;
    struct stat wStat;
    string wPathName;

    // ディレクトリを開く
    pDir = opendir( dirname.c_str() );
    if ( NULL == pDir ) {
        printf( "Error : in %s directoryt\n", dirname.c_str() );
        return ;
    }

    pEnt = readdir( pDir );
    while ( pEnt ) {
        // .と..は処理しない
        if ( strcmp( pEnt->d_name, "." ) &&
            strcmp( pEnt->d_name, ".." ) ) {
            wPathName = dirname + "/" + pEnt->d_name;
            // ファイルの情報を取得
            if ( stat( wPathName.c_str(), &wStat ) ) {
                printf( "Failed to get stat %s \n", wPathName.c_str() );
                break;
            }
            if ( S_ISDIR( wStat.st_mode ) ) {
                // ディレクトリの場合
                print_cnt( cnt );
                printf( "Directory - %s\n", pEnt->d_name );
                foo( cnt + 1, wPathName );
            }
            else {
                // ファイルの場合
                print_cnt( cnt );
                printf( "File - %s\n", pEnt->d_name );
            }
        }
        // 次のファイルを検索する
        pEnt = readdir( pDir );
    }

    // ハンドルを閉じる
    closedir( pDir );
}

int main()
{
    foo( 0, "." );
    return 0;
}

(上記のプログラムはLinuxで動作確認をしています。)

重複の確認

上記のようにディレクトリを再帰的に走査すると、ディレクトリツリーがループを形成している場合に、無限ループに陥る。 つまり、直系の先祖に対するハードリンク(もしくはシンボリックリンク)が存在する場合だ。

Unixではこの対策を怠ると間違いなくひどい目にあう。

とりあえず「struct stat」構造体のst_inoメンバがiノード番号を保持しているため、 この番号を確認して、すでに同じものを処理してないかどうかを確認すればいい。

Windowsの方はいちいち重複を確認する必要はないはずだ。上記のコードではショートカットはただの「.lnk」という拡張子を持つファイルとして取り扱われる。 でもって、Windows2000以上のNTFSでサポートされるようになったハードリンクは、ディレクトリに対しては作成できない。よって、最悪でも無限ループに陥ることはない。