[C言語] フォルダ内のファイルを検索する方法を解説
C言語でフォルダ内のファイルを検索するには、主にPOSIX標準のディレクトリ操作関数を使用します。
具体的には、opendir
関数でディレクトリを開き、readdir
関数でディレクトリ内のエントリを順に読み取ります。
各エントリはstruct dirent
型の構造体として返され、その中のd_name
メンバーを使用してファイル名を取得します。
検索条件に合致するファイルを見つけたら、必要に応じて処理を行います。
最後にclosedir
関数でディレクトリを閉じることを忘れないようにしましょう。
ファイル検索の実装
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;
}
このプログラムは、ハッシュテーブルを使用してファイル名を効率的に検索します。
大量のファイルがある場合でも、高速に検索を行うことができます。
まとめ
C言語でのファイル検索は、さまざまな方法で実装でき、応用することで多くの便利な機能を実現できます。
この記事では、ファイル検索の基本的な実装方法から、エラーハンドリング、応用例、よくある質問までを網羅しました。
これを機に、C言語でのファイル操作をさらに深く学び、実際のプロジェクトで活用してみてください。