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

Windows関連

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

半透明ウインドウの性能

bootfont.bin

キャビネット形式

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

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

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

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

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

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

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

ディレクトリの読み込み

2005年10月20日公開

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でサポートされるようになったハードリンクは、ディレクトリに対しては作成できない。よって、最悪でも無限ループに陥ることはない。


連絡先 - サイトマップ - 更新履歴
Copyright (C)  2000 - 2016 nabiki_t All Rights Reserved.