Qt QFileInfoList徹底解説:ファイル情報取得の基本から応用まで

2025-06-01

  1. QFileInfoとは? まず、QFileInfoクラスについて理解することが重要です。QFileInfoは、単一のファイルまたはディレクトリに関する詳細な情報(ファイル名、パス、サイズ、最終更新日時、パーミッション、種類(ファイルかディレクトリか、シンボリックリンクかなど)など)を提供するクラスです。ファイルシステム上の特定のエントリに関する属性を取得するのに使われます。

  2. 使い方(概念) 一般的な使用の流れは以下のようになります。

    1. QDirオブジェクトを作成し、対象のディレクトリを指定します。
    2. QDirオブジェクトのentryInfoList()メソッドを呼び出し、ディレクトリ内のコンテンツのQFileInfoListを取得します。
    3. 取得したQFileInfoListをイテレート(ループ処理)して、個々のQFileInfoオブジェクトにアクセスします。
    4. QFileInfoオブジェクトから、ファイル名、サイズ、パスなどの必要な情報を取得し、それぞれのファイルに対して処理を行います。


ディレクトリが存在しない、またはアクセス権がない

よくあるエラー/問題

  • アプリケーションがクラッシュしたり、予期しない動作をしたりする。
  • 意図したファイルやディレクトリがリストに含まれていない。
  • QDir::entryInfoList()を呼び出しても、空のQFileInfoListが返ってくる。

原因

  • パスが間違っている(タイプミス、相対パスの基準が異なるなど)。
  • アプリケーションにそのディレクトリへの読み取り権限がない。
  • 指定されたパスのディレクトリが存在しない。

トラブルシューティング

    • QDirオブジェクトを作成する際に指定したパスが正しいことを確認します。絶対パスを使用するか、QCoreApplication::applicationDirPath()などを使って基準となるパスを明確にします。
    • QDir::exists()を使って、ディレクトリが実際に存在するかどうかを事前に確認します。
    QDir dir("C:/MyData/NonExistentFolder"); // 例:存在しないパス
    if (!dir.exists()) {
        qDebug() << "エラー: ディレクトリが存在しません。";
        // エラー処理、またはユーザーに通知
        return;
    }
    QFileInfoList entries = dir.entryInfoList();
    
  1. アクセス権の確認

    • 対象のディレクトリに対するアプリケーションの読み取り権限を確認します。特に、管理者権限が必要なシステムディレクトリや、UAC (User Account Control) の影響を受けるWindows環境で発生しやすいです。
    • Qtのコードから直接アクセス権をチェックすることは難しいですが、問題が発生した場合は、手動でファイルエクスプローラーなどからアクセス権を確認してください。
  2. フィルターの確認

    • QDir::entryInfoList()は、QDir::FiltersQDir::SortFlagsという引数を取ることができます。これらが意図しないフィルター(例: QDir::Filesしか指定していないのにディレクトリを探している)になっている場合、期待する結果が得られません。
    • 特に、. (カレントディレクトリ) や .. (親ディレクトリ) を除外したい場合は、QDir::NoDotAndDotDotフィルターを使用します。
    QDir dir("/path/to/your/directory");
    QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
    // これにより、ファイルとディレクトリの両方を取得し、'.'と'..'を除外する
    

よくあるエラー/問題

  • QFileInfo::fileName()が文字化けする。
  • ファイルが存在するのにQFileInfo::exists()falseを返す。

原因

  • 特にWindows環境で、ANSI APIとUnicode APIの混在が原因で発生することがあります。
  • 古いQtバージョンや特定の環境設定では、ファイルパスのエンコーディングがシステムデフォルト(非UTF-8)になっている場合があり、Qtの内部的なUTF-16/UCS-2表現と一致しないために問題が発生します。

トラブルシューティング

  1. QStringの正しい使用

    • Qtではパスを扱う際にQStringを使用することが非常に重要です。C++標準のstd::stringを直接使用すると、エンコーディングの問題が発生しやすくなります。
    • 外部からパス文字列を取得する場合(例: std::stringで渡された場合)は、適切にQString::fromLocal8Bit()QString::fromStdString()などを使用してQStringに変換してください。
    • ただし、ファイルシステムパスの場合は、通常QDirQFileなどのQtクラスに直接QStringを渡すことで、Qtが内部的に適切なエンコーディングで処理します。
  2. コンパイラと環境の設定

    • 特にWindows + MinGW環境などでは、ビルド設定やシステムロケールが影響する場合があります。Qtのデフォルトの動作で問題が解決しない場合は、Qt CreatorやVisual Studioのプロジェクト設定でエンコーディング関連のオプション(例: C++ソースファイルのエンコーディング)を確認してください。
    • Windowsでは、非Unicodeプログラムの言語設定(コントロールパネル > 地域 > 管理タブ > Unicode対応ではないプログラムの言語)も影響する場合があります。

シンボリックリンクの扱い

よくあるエラー/問題

  • QFileInfo::filePath()QFileInfo::symLinkTarget()の違いが不明確。
  • QFileInfo::isSymLink()が期待と異なる結果を返す。

原因

  • QFileInfoは、シンボリックリンク自体と、そのリンクが指す実際のファイル/ディレクトリの両方の情報を提供できるため、どちらの情報を取得したいかによってメソッドを使い分ける必要があります。
  • シンボリックリンクの動作は、OSによって異なる場合があります。

トラブルシューティング

  1. isSymLink()とisDir()/isFile()の理解

    • QFileInfo::isSymLink()は、そのパスがシンボリックリンクであるかどうかを判断します。
    • QFileInfo::isDir()QFileInfo::isFile()は、シンボリックリンクが指すターゲットがディレクトリかファイルかを判断します。
      QFileInfo fi("/path/to/symlink");
      if (fi.isSymLink()) {
          qDebug() << "これはシンボリックリンクです。";
          qDebug() << "リンク先: " << fi.symLinkTarget(); // リンク先パス
          if (fi.isDir()) {
              qDebug() << "リンク先はディレクトリです。";
          } else if (fi.isFile()) {
              qDebug() << "リンク先はファイルです。";
          }
      }
      
  2. canonicalFilePath()の使用

    • シンボリックリンクを解決して、実際のファイルやディレクトリの正規パスを取得したい場合は、QFileInfo::canonicalFilePath()を使用します。
      QFileInfo fi("/path/to/symlink");
      QString actualPath = fi.canonicalFilePath(); // シンボリックリンクを解決したパス
      qDebug() << "実際のパス: " << actualPath;
      

パフォーマンスの問題(大規模なディレクトリ)

よくあるエラー/問題

  • 非常に大きなディレクトリ(多数のファイルやサブディレクトリを含む)をentryInfoList()で読み込むと、アプリケーションがフリーズしたり、応答しなくなる。

原因

  • entryInfoList()は、一度にすべてのファイル情報をメモリに読み込むため、ファイル数が多いと大量のメモリを消費し、処理に時間がかかります。

トラブルシューティング

  1. QDirIteratorの使用

    • 大規模なディレクトリを処理する場合は、QDirIteratorを使用することを強く推奨します。QDirIteratorはファイルを一つずつ取得するため、メモリ使用量を抑え、必要な情報だけを順次処理できます。
    QDirIterator it("/path/to/large/directory", QDirIterator::Subdirectories); // サブディレクトリも含む
    while (it.hasNext()) {
        QString filePath = it.next();
        QFileInfo fileInfo(filePath);
        // ここでfileInfoを使って個々のファイル処理を行う
        qDebug() << fileInfo.fileName();
    }
    
  2. フィルターの最適化

    • QDir::entryInfoList()を使用する場合でも、必要なファイルやディレクトリだけをフィルタリングすることで、パフォーマンスを改善できます。
    • 例: 特定の拡張子のファイルのみ、ディレクトリのみなど。
    QDir dir("/path/to/directory");
    QStringList nameFilters;
    nameFilters << "*.txt" << "*.log"; // .txt と .log ファイルのみ
    QFileInfoList entries = dir.entryInfoList(nameFilters, QDir::Files);
    
  3. 別スレッドでの処理

    • UIスレッドでのフリーズを避けるため、ファイルシステムのスキャンなどの時間のかかる処理は、QtConcurrent::run()QThreadを使用して別のスレッドで行い、結果をUIスレッドにシグナルで通知するように設計します。

よくあるエラー/問題

  • QFileInfoListから取得したQFileInfoオブジェクトを、リストのスコープ外で使おうとすると、情報が古くなったり、無効になったりする。

原因

  • しかし、ファイルシステムの状態は時間とともに変化するため、取得したQFileInfo情報が古くなる可能性があります。
  • QFileInfoは値クラス(value class)であり、コピーによって完全な情報が複製されます。QFileInfoListも同様に、内部にQFileInfoオブジェクトのコピーを保持します。

トラブルシューティング

  1. 必要なタイミングでの情報取得

    • 常に最新のファイル情報が必要な場合は、QFileInfoオブジェクトを再作成するか、QFileInfo::refresh()を呼び出して情報を更新します。
      QFileInfo fileInfo("/path/to/file.txt");
      // 何らかの処理...
      fileInfo.refresh(); // 最新の情報に更新
      if (fileInfo.exists()) {
          qDebug() << "ファイルはまだ存在します。";
      }
      
  2. QFileInfoを値として扱う

    • QFileInfoは軽量なオブジェクトであり、コピーコストは通常問題になりません。QFileInfoListから取得したQFileInfoは、必要であればコピーして別の場所で保持しても問題ありません。


ディレクトリ内のファイルとサブディレクトリの一覧を取得する

最も一般的な使い方です。特定のディレクトリ内のコンテンツ(ファイルとサブディレクトリ)をリストアップします。

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QFileInfoList>
#include <QDebug> // デバッグ出力用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 取得したいディレクトリのパスを指定します。
    // 例: アプリケーションの実行ディレクトリ
    QString path = QCoreApplication::applicationDirPath();
    // もしくは、任意のパスを指定することもできます。
    // QString path = "C:/Users/YourUser/Documents"; // Windowsの場合
    // QString path = "/home/youruser/Documents";   // Linux/macOSの場合

    QDir dir(path);

    // ディレクトリが存在するかどうかを確認
    if (!dir.exists()) {
        qDebug() << "エラー: 指定されたディレクトリが存在しません:" << path;
        return 1; // エラー終了
    }

    qDebug() << "ディレクトリ内のコンテンツをリストアップ中:" << dir.absolutePath();

    // entryInfoList() を使って QFileInfoList を取得します。
    // QDir::Dirs       : ディレクトリを含める
    // QDir::Files      : ファイルを含める
    // QDir::NoDotAndDotDot : "." (カレントディレクトリ) と ".." (親ディレクトリ) を除外
    // QDir::NoSymLinks : シンボリックリンクを除外 (リンク先の実体を取得する)
    // QDir::SortFlags  : ソート順 (ここでは名前順)
    QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot,
                                              QDir::Name); // 名前順でソート

    // 取得した QFileInfoList をループで処理します
    foreach (const QFileInfo &entryInfo, entries) {
        if (entryInfo.isDir()) {
            qDebug() << " [DIR] " << entryInfo.fileName();
        } else if (entryInfo.isFile()) {
            qDebug() << " [FIL] " << entryInfo.fileName()
                     << " (サイズ: " << entryInfo.size() << "バイト)";
        }
        // 他にも、isSymLink()、isReadable()、lastModified() など、様々な情報が取得できます
    }

    return 0; // 正常終了
}

解説

  • entryInfo.size(): ファイルのサイズをバイト単位で取得します。
  • entryInfo.fileName(): ファイル名(またはディレクトリ名)を取得します。
  • entryInfo.isDir() / entryInfo.isFile(): それぞれ、そのエントリがディレクトリかファイルかを判定します。
  • foreach (const QFileInfo &entryInfo, entries): 取得した QFileInfoList をループで回し、各 QFileInfo オブジェクトにアクセスします。const & を使うことで、不必要なコピーを避け、効率的に処理できます。
  • dir.entryInfoList(...): ここが核心です。
    • 第1引数: QDir::Filters を指定します。| (OR) で複数のフィルターを組み合わせることができます。
      • QDir::Dirs: ディレクトリを含めます。
      • QDir::Files: ファイルを含めます。
      • QDir::NoDotAndDotDot: リストから ... を除外します。これらは通常、望ましくない情報です。
    • 第2引数: QDir::SortFlags を指定します。
      • QDir::Name: ファイル/ディレクトリ名を基準にソートします。
  • dir.exists(): ディレクトリが存在するかどうかを確認します。これがないと、存在しないパスに対して操作しようとしたときにエラーになる可能性があります。
  • QDir dir(path): 指定されたパスで QDir オブジェクトを初期化します。
  • QCoreApplication::applicationDirPath(): 現在実行中のアプリケーションのディレクトリパスを取得します。これを使えば、コードを実行する環境に依存せず、常に存在するディレクトリを対象にできます。

特定の条件でファイルをフィルタリングする

QDir::entryInfoList() のフィルター機能を使って、特定の種類のファイルだけを抽出する例です。

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QFileInfoList>
#include <QDebug>
#include <QStringList> // 拡張子フィルター用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString path = QCoreApplication::applicationDirPath();
    QDir dir(path);

    if (!dir.exists()) {
        qDebug() << "エラー: ディレクトリが存在しません:" << path;
        return 1;
    }

    qDebug() << "現在のディレクトリから画像ファイル (.png, .jpg) を検索中:" << dir.absolutePath();

    // フィルタリングしたい拡張子リストを作成します
    QStringList nameFilters;
    nameFilters << "*.png" << "*.jpg" << "*.jpeg" << "*.gif"; // 画像ファイル拡張子

    // entryInfoList() のオーバーロードを使って、名前フィルターと種類フィルターを適用
    QFileInfoList imageFiles = dir.entryInfoList(nameFilters,
                                                 QDir::Files, // ファイルのみを対象
                                                 QDir::Name); // 名前順ソート

    if (imageFiles.isEmpty()) {
        qDebug() << "画像ファイルは見つかりませんでした。";
    } else {
        foreach (const QFileInfo &fileInfo, imageFiles) {
            qDebug() << " 見つかった画像: " << fileInfo.fileName()
                     << " (パス: " << fileInfo.absoluteFilePath() << ")";
        }
    }

    return 0;
}

解説

  • dir.entryInfoList(nameFilters, QDir::Files, QDir::Name):
    • 第1引数: QStringList で名前フィルターを渡します。
    • 第2引数: QDir::Files を指定し、ファイルのみを対象とします。
    • 第3引数: ソート順を指定します。
  • QStringList nameFilters;: 複数の拡張子をリストとして定義します。ワイルドカード * を使用できます。

ユーザーがファイル選択ダイアログで複数のファイルを選択した場合に、その結果を QFileInfoList として受け取り、処理する例です。

注意
この例はGUIアプリケーションでのみ動作します。QCoreApplication ではなく QApplication を使用し、QFileDialog はGUIモジュールに属します。

// .pro ファイルに 'QT += widgets' を追加してください

#include <QApplication> // GUIアプリケーション用
#include <QFileDialog>  // ファイルダイアログ用
#include <QFileInfo>
#include <QFileInfoList>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // ファイル選択ダイアログを開きます
    // "Select Files" : ダイアログのタイトル
    // QDir::homePath() : 初期表示ディレクトリ (ここではユーザーのホームディレクトリ)
    // "*.txt *.log" : フィルタ (テキストファイルとログファイルのみ表示)
    QStringList selectedFilePaths = QFileDialog::getOpenFileNames(
                                        nullptr,
                                        "ファイルを選択してください",
                                        QDir::homePath(),
                                        "テキストファイル (*.txt);;ログファイル (*.log);;全てのファイル (*.*)");

    if (selectedFilePaths.isEmpty()) {
        qDebug() << "ファイルは選択されませんでした。";
    } else {
        qDebug() << "選択されたファイル:";
        // 取得した QStringList から QFileInfoList を作成します
        QFileInfoList selectedFileInfos;
        foreach (const QString &filePath, selectedFilePaths) {
            selectedFileInfos.append(QFileInfo(filePath)); // 各パスから QFileInfo を作成し追加
        }

        // QFileInfoList をループで処理
        foreach (const QFileInfo &fileInfo, selectedFileInfos) {
            if (fileInfo.exists()) {
                qDebug() << " - 名前: " << fileInfo.fileName()
                         << ", フルパス: " << fileInfo.absoluteFilePath()
                         << ", サイズ: " << fileInfo.size() << "バイト";
            } else {
                qDebug() << " - エラー: 選択されたファイルが見つかりません:" << fileInfo.fileName();
            }
        }
    }

    return 0;
}
  • fileInfo.exists(): ファイルが実際に存在するかどうかを確認することは重要です。ユーザーが古いパスを選択したり、ファイルが移動/削除されたりする可能性があるためです。
  • selectedFileInfos.append(QFileInfo(filePath));: 取得したパス文字列 (QString) を元に QFileInfo オブジェクトを作成し、QFileInfoList に追加しています。このようにして、QFileInfoList を手動で構築することも可能です。
  • QFileDialog::getOpenFileNames(...): ユーザーに複数のファイルを選択させるためのスタティックメソッドです。
    • 戻り値は QStringList ですが、これは選択されたファイルのパスのリストです。
  • QApplication a(argc, argv);: GUIアプリケーションの開始に必要なオブジェクトです。


QDirIterator の使用 (大規模なディレクトリ走査に最適)

利点

  • 柔軟な走査
    サブディレクトリを含めるか、特定の深さまで走査するかなどを細かく制御できる。
  • パフォーマンス
    必要な情報をオンデマンドで取得するため、初期読み込みが速い。
  • メモリ効率
    大量のファイルがある場合でもメモリ消費が少ない。

欠点

  • 一度にすべての情報を取得できないため、リスト全体をソートしたり、特定のインデックスでアクセスしたりするのは難しい。

使用例

#include <QCoreApplication>
#include <QDirIterator>
#include <QFileInfo>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString path = QCoreApplication::applicationDirPath();
    qDebug() << "QDirIterator でディレクトリを走査中:" << path;

    // QDirIterator を作成
    // path: 走査を開始するパス
    // QDirIterator::Subdirectories: サブディレクトリも再帰的に走査
    // QDirIterator::FollowSymlinks: シンボリックリンクを辿る (必要に応じて変更)
    QDirIterator it(path, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);

    while (it.hasNext()) { // 次のエントリがあるか確認
        it.next(); // 次のエントリに進む

        // 現在のエントリの QFileInfo を取得
        // QDirIterator::fileInfo() は、名前ではなく QFileInfo を直接返すので便利
        QFileInfo entryInfo = it.fileInfo();

        // "." と ".." は通常除外したい
        if (entryInfo.fileName() == "." || entryInfo.fileName() == "..") {
            continue;
        }

        if (entryInfo.isDir()) {
            qDebug() << " [DIR] " << entryInfo.absoluteFilePath();
        } else if (entryInfo.isFile()) {
            qDebug() << " [FIL] " << entryInfo.absoluteFilePath()
                     << " (サイズ: " << entryInfo.size() << "バイト)";
        }
    }

    return 0;
}

QDir::entryList() と手動での QFileInfo 作成

利点

  • QFileDialog::getOpenFileNames() の戻り値など、既に QStringList が手元にある場合に、それを元に QFileInfo を作成するのに便利。
  • ファイル名だけが必要な場合は、QFileInfo オブジェクトの完全な情報が不要なため、若干軽量になる。

欠点

  • QDir::entryInfoList() が一度の呼び出しで複数の属性を取得するのに対し、QFileInfo の作成ごとにファイルシステムへの問い合わせが発生する可能性がある。

使用例

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QStringList>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString path = QCoreApplication::applicationDirPath();
    QDir dir(path);

    if (!dir.exists()) {
        qDebug() << "エラー: ディレクトリが存在しません:" << path;
        return 1;
    }

    qDebug() << "QDir::entryList() と QFileInfo 手動作成でコンテンツをリストアップ中:" << dir.absolutePath();

    // entryList() を使ってファイル/ディレクトリ名のリストを取得
    // QDir::Dirs と QDir::Files を指定し、名前順ソート
    QStringList entries = dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name);

    foreach (const QString &entryName, entries) {
        // 各エントリ名に対して QFileInfo を作成
        // dir.absoluteFilePath(entryName) で完全なパスを取得するのが重要
        QFileInfo entryInfo(dir.absoluteFilePath(entryName));

        if (entryInfo.isDir()) {
            qDebug() << " [DIR] " << entryInfo.fileName();
        } else if (entryInfo.isFile()) {
            qDebug() << " [FIL] " << entryInfo.fileName()
                     << " (サイズ: " << entryInfo.size() << "バイト)";
        }
    }

    return 0;
}
  • Linux/Unix/macOS
    opendir, readdir, stat, lstat などの POSIX API 関数。
  • Windows
    FindFirstFileW, FindNextFileW, GetFileAttributesExW などの WinAPI 関数。

利点

  • 特定のプラットフォームで最高のパフォーマンスを引き出せる可能性がある(ただし、最適化には専門知識が必要)。
  • Qt が提供しない、低レベルのファイルシステム機能にアクセスできる。

欠点

  • 通常はQtのファイルシステムクラスで十分であり、よほどの理由がない限り推奨されない
  • Qt の恩恵を受けられない
    QString のパス処理、エラーハンドリングなど、Qt の便利な機能が使えない。
  • 学習コストが高い
    各OSのAPIに関する深い知識が必要。
  • 移植性がない
    プラットフォームごとに異なるコードを書く必要がある。
// Windows API を使用する概念的な例
#ifdef Q_OS_WIN
#include <Windows.h>
#include <QString>
#include <QDebug>

void listFilesWinAPI(const QString& path) {
    WIN32_FIND_DATAW findData; // W はワイド文字 (Unicode) 版
    HANDLE hFind = FindFirstFileW((path + L"\\*").toStdWString().c_str(), &findData);

    if (hFind == INVALID_HANDLE_VALUE) {
        qDebug() << "エラー: ディレクトリを開けません。";
        return;
    }

    do {
        // "." と ".." は除外
        if (wcscmp(findData.cFileName, L".") == 0 || wcscmp(findData.cFileName, L"..") == 0) {
            continue;
        }

        QString fileName = QString::fromWCharArray(findData.cFileName);
        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            qDebug() << " [DIR] " << fileName;
        } else {
            qDebug() << " [FIL] " << fileName;
            // ファイルサイズなど、さらに詳細な情報を取得するには追加のAPI呼び出しが必要
        }
    } while (FindNextFileW(hFind, &findData) != 0);

    FindClose(hFind);
}

// int main(...) で呼び出す
// listFilesWinAPI(QCoreApplication::applicationDirPath());
#endif
  • 非常に特殊なプラットフォーム固有の要件
    プラットフォームネイティブのAPIを使用しますが、これは最後の手段として検討すべきです。
  • ファイル名リストのみが必要で、その後個別に QFileInfo を構築したい場合
    QDir::entryList() と手動での QFileInfo 作成が選択肢になります。
  • 大規模なディレクトリ走査やメモリ効率が重要な場合
    QDirIterator が最適な代替手段です。
  • ほとんどのユースケース
    QFileInfoListQDir::entryInfoList() は非常に便利で十分です。