[C言語] フォルダ内のファイルを検索する方法を解説

C言語でフォルダ内のファイルを検索するには、主にPOSIX標準のディレクトリ操作関数を使用します。

具体的には、opendir関数でディレクトリを開き、readdir関数でディレクトリ内のエントリを順に読み取ります。

各エントリはstruct dirent型の構造体として返され、その中のd_nameメンバーを使用してファイル名を取得します。

検索条件に合致するファイルを見つけたら、必要に応じて処理を行います。

最後にclosedir関数でディレクトリを閉じることを忘れないようにしましょう。

この記事でわかること
  • ファイル名や拡張子による検索方法
  • 再帰的なディレクトリ検索の実装
  • ファイル操作時のエラーハンドリング手法
  • 特定のファイルを自動的にバックアップする方法
  • ファイル検索が遅い場合の対処法とOS依存の問題解決方法

目次から探す

ファイル検索の実装

C言語でフォルダ内のファイルを検索する方法について解説します。

ここでは、ファイル名による検索、拡張子によるフィルタリング、再帰的なディレクトリ検索の3つの方法を紹介します。

ファイル名による検索

ファイル名による検索は、指定した名前のファイルをディレクトリ内で探す方法です。

以下に、基本的なサンプルコードを示します。

#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main() {
    DIR *dir;
    struct dirent *entry;
    const char *targetFileName = "example.txt"; // 検索するファイル名
    // ディレクトリを開く
    dir = opendir(".");
    if (dir == NULL) {
        perror("ディレクトリを開けません");
        return 1;
    }
    // ディレクトリ内のエントリを読み込む
    while ((entry = readdir(dir)) != NULL) {
        // ファイル名が一致するか確認
        if (strcmp(entry->d_name, targetFileName) == 0) {
            printf("ファイルが見つかりました: %s\n", entry->d_name);
        }
    }
    // ディレクトリを閉じる
    closedir(dir);
    return 0;
}

このプログラムは、カレントディレクトリ内でexample.txtというファイルを探します。

見つかった場合、そのファイル名を出力します。

拡張子によるフィルタリング

拡張子によるフィルタリングは、特定の拡張子を持つファイルのみを検索する方法です。

以下にサンプルコードを示します。

#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main() {
    DIR *dir;
    struct dirent *entry;
    const char *extension = ".txt"; // 検索する拡張子
    // ディレクトリを開く
    dir = opendir(".");
    if (dir == NULL) {
        perror("ディレクトリを開けません");
        return 1;
    }
    // ディレクトリ内のエントリを読み込む
    while ((entry = readdir(dir)) != NULL) {
        // 拡張子が一致するか確認
        if (strstr(entry->d_name, extension) != NULL) {
            printf("ファイルが見つかりました: %s\n", entry->d_name);
        }
    }
    // ディレクトリを閉じる
    closedir(dir);
    return 0;
}

このプログラムは、カレントディレクトリ内で.txt拡張子を持つファイルを探し、見つかったファイル名を出力します。

再帰的なディレクトリ検索

再帰的なディレクトリ検索は、サブディレクトリを含むすべてのディレクトリを探索する方法です。

以下にサンプルコードを示します。

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
void searchDirectory(const char *dirName) {
    DIR *dir;
    struct dirent *entry;
    struct stat statbuf;
    // ディレクトリを開く
    dir = opendir(dirName);
    if (dir == NULL) {
        perror("ディレクトリを開けません");
        return;
    }
    // ディレクトリ内のエントリを読み込む
    while ((entry = readdir(dir)) != NULL) {
        char path[1024];
        snprintf(path, sizeof(path), "%s/%s", dirName, entry->d_name);
        // エントリの情報を取得
        if (stat(path, &statbuf) == 0) {
            // ディレクトリの場合、再帰的に検索
            if (S_ISDIR(statbuf.st_mode)) {
                if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                    searchDirectory(path);
                }
            } else {
                printf("ファイルが見つかりました: %s\n", path);
            }
        }
    }
    // ディレクトリを閉じる
    closedir(dir);
}
int main() {
    searchDirectory("."); // カレントディレクトリから検索開始
    return 0;
}

このプログラムは、カレントディレクトリから開始して、すべてのサブディレクトリを再帰的に探索し、見つかったファイルのパスを出力します。

再帰的な検索は、ディレクトリ構造が深い場合に便利です。

エラーハンドリング

C言語でファイルを操作する際には、さまざまなエラーが発生する可能性があります。

ここでは、ファイル操作時の一般的なエラー、エラーコードの取得と処理、ログ出力によるデバッグについて解説します。

ファイル操作時の一般的なエラー

ファイル操作中に発生する一般的なエラーには、以下のようなものがあります。

スクロールできます
エラーの種類説明
ファイルが見つからない指定したファイルが存在しない場合に発生します。
アクセス権限がないファイルに対する読み取りまたは書き込みの権限がない場合に発生します。
ディスク容量不足ファイルを書き込む際にディスクの空き容量が不足している場合に発生します。

これらのエラーは、適切にハンドリングすることで、プログラムの安定性を向上させることができます。

エラーコードの取得と処理

C言語では、ファイル操作関数がエラーを返す場合、通常はerrnoというグローバル変数にエラーコードが設定されます。

以下に、エラーコードの取得と処理の例を示します。

#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
    FILE *file = fopen("nonexistent.txt", "r"); // 存在しないファイルを開く
    if (file == NULL) {
        // エラーコードを取得
        int errnum = errno;
        // エラーメッセージを表示
        fprintf(stderr, "ファイルを開けません: %s\n", strerror(errnum));
        return 1;
    }
    fclose(file);
    return 0;
}

このプログラムは、存在しないファイルを開こうとし、エラーが発生した場合にerrnoを使用してエラーコードを取得し、strerror関数でエラーメッセージを表示します。

ログ出力によるデバッグ

エラーハンドリングの一環として、ログ出力を行うことで、プログラムの動作を追跡しやすくなります。

以下に、ログ出力の例を示します。

#include <stdio.h>
#include <errno.h>
#include <string.h>
void logError(const char *message) {
    FILE *logFile = fopen("error.log", "a"); // ログファイルを追記モードで開く
    if (logFile != NULL) {
        fprintf(logFile, "エラー: %s\n", message);
        fclose(logFile);
    }
}
int main() {
    FILE *file = fopen("nonexistent.txt", "r"); // 存在しないファイルを開く
    if (file == NULL) {
        int errnum = errno;
        char errorMessage[256];
        snprintf(errorMessage, sizeof(errorMessage), "ファイルを開けません: %s", strerror(errnum));
        logError(errorMessage); // エラーメッセージをログに出力
        return 1;
    }
    fclose(file);
    return 0;
}

このプログラムは、エラーが発生した際にエラーメッセージをerror.logファイルに出力します。

ログを残すことで、後から問題の原因を特定しやすくなります。

応用例

C言語でのファイル検索を応用することで、さまざまな便利な機能を実現できます。

ここでは、特定のファイルを自動的にバックアップする方法、ファイルの更新日時によるフィルタリング、大量のファイルを効率的に検索する方法について解説します。

特定のファイルを自動的にバックアップ

特定のファイルを自動的にバックアップするには、ファイルをコピーする機能を実装します。

以下に、ファイルをバックアップするサンプルコードを示します。

#include <stdio.h>
int backupFile(const char *source, const char *destination) {
    FILE *srcFile = fopen(source, "rb");
    FILE *destFile = fopen(destination, "wb");
    if (srcFile == NULL || destFile == NULL) {
        perror("ファイルを開けません");
        return 1;
    }
    char buffer[1024];
    size_t bytesRead;
    while ((bytesRead = fread(buffer, 1, sizeof(buffer), srcFile)) > 0) {
        fwrite(buffer, 1, bytesRead, destFile);
    }
    fclose(srcFile);
    fclose(destFile);
    return 0;
}
int main() {
    const char *sourceFile = "example.txt";
    const char *backupFile = "example_backup.txt";
    if (backupFile(sourceFile, backupFile) == 0) {
        printf("バックアップが完了しました: %s\n", backupFile);
    }
    return 0;
}

このプログラムは、example.txtというファイルをexample_backup.txtとしてバックアップします。

ファイルをバイナリモードで読み書きすることで、テキストファイルだけでなくバイナリファイルも扱えます。

ファイルの更新日時によるフィルタリング

ファイルの更新日時を利用して、特定の期間内に更新されたファイルをフィルタリングすることができます。

以下に、更新日時を取得してフィルタリングするサンプルコードを示します。

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
int main() {
    DIR *dir;
    struct dirent *entry;
    struct stat statbuf;
    time_t currentTime = time(NULL);
    const int days = 7; // 7日以内に更新されたファイルを検索
    dir = opendir(".");
    if (dir == NULL) {
        perror("ディレクトリを開けません");
        return 1;
    }
    while ((entry = readdir(dir)) != NULL) {
        if (stat(entry->d_name, &statbuf) == 0) {
            double seconds = difftime(currentTime, statbuf.st_mtime);
            if (seconds <= days * 24 * 60 * 60) {
                printf("最近更新されたファイル: %s\n", entry->d_name);
            }
        }
    }
    closedir(dir);
    return 0;
}

このプログラムは、カレントディレクトリ内で過去7日以内に更新されたファイルを検索し、そのファイル名を出力します。

大量のファイルを効率的に検索する方法

大量のファイルを効率的に検索するには、データ構造やアルゴリズムを工夫することが重要です。

以下に、ハッシュテーブルを利用してファイル名を効率的に検索する方法を示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#define TABLE_SIZE 100
typedef struct Node {
    char *fileName;
    struct Node *next;
} Node;
Node *hashTable[TABLE_SIZE];
unsigned int hash(const char *str) {
    unsigned int hash = 0;
    while (*str) {
        hash = (hash << 5) + *str++;
    }
    return hash % TABLE_SIZE;
}
void insertFileName(const char *fileName) {
    unsigned int index = hash(fileName);
    Node *newNode = malloc(sizeof(Node));
    newNode->fileName = strdup(fileName);
    newNode->next = hashTable[index];
    hashTable[index] = newNode;
}
int searchFileName(const char *fileName) {
    unsigned int index = hash(fileName);
    Node *node = hashTable[index];
    while (node) {
        if (strcmp(node->fileName, fileName) == 0) {
            return 1; // 見つかった
        }
        node = node->next;
    }
    return 0; // 見つからない
}
int main() {
    DIR *dir;
    struct dirent *entry;
    dir = opendir(".");
    if (dir == NULL) {
        perror("ディレクトリを開けません");
        return 1;
    }
    while ((entry = readdir(dir)) != NULL) {
        insertFileName(entry->d_name);
    }
    closedir(dir);
    const char *targetFile = "example.txt";
    if (searchFileName(targetFile)) {
        printf("ファイルが見つかりました: %s\n", targetFile);
    } else {
        printf("ファイルが見つかりません: %s\n", targetFile);
    }
    return 0;
}

このプログラムは、ハッシュテーブルを使用してファイル名を効率的に検索します。

大量のファイルがある場合でも、高速に検索を行うことができます。

よくある質問

ファイル検索が遅い場合の対処法は?

ファイル検索が遅い場合、以下の方法を試してみてください。

  • データ構造の最適化: ハッシュテーブルやバイナリサーチツリーを使用して、検索を高速化します。
  • 非同期処理の導入: マルチスレッドを利用して、複数のディレクトリを同時に検索することで、処理時間を短縮します。
  • キャッシュの利用: 以前の検索結果をキャッシュして、同じ検索を繰り返す際に再利用します。

特定のOSで動作しない場合の原因は?

特定のOSで動作しない場合、以下の原因が考えられます。

  • APIの違い: OSによってファイル操作のAPIが異なるため、コードが互換性を持たないことがあります。

例:opendirreaddirの使用方法が異なる。

  • ファイルシステムの違い: ファイルシステムの違いにより、ファイル名の扱いやパスの表記が異なることがあります。
  • コンパイラの違い: 使用しているコンパイラが異なる場合、特定の関数やライブラリがサポートされていないことがあります。

再帰的な検索でスタックオーバーフローを防ぐには?

再帰的な検索でスタックオーバーフローを防ぐためには、以下の方法を検討してください。

  • 再帰の深さを制限: 再帰の深さを制限することで、スタックの使用量を抑えます。
  • イテレーティブなアプローチ: 再帰を使用せず、スタックやキューを用いてイテレーティブにディレクトリを探索します。
  • スタックサイズの調整: 必要に応じて、プログラムのスタックサイズを増やすことで、再帰の深さを確保します。

まとめ

C言語でのファイル検索は、さまざまな方法で実装でき、応用することで多くの便利な機能を実現できます。

この記事では、ファイル検索の基本的な実装方法から、エラーハンドリング、応用例、よくある質問までを網羅しました。

これを機に、C言語でのファイル操作をさらに深く学び、実際のプロジェクトで活用してみてください。

  • URLをコピーしました!
目次から探す