Qtファイル存在確認のベストプラクティス: QFileInfo::exists()とエラー処理

2025-05-31

具体的には、この関数は以下のいずれかの状態であれば true を返します。

  • QFileInfo オブジェクトが既存のシンボリックリンクを参照している場合(リンク先が存在するかどうかは問いません)
  • QFileInfo オブジェクトが既存のディレクトリを参照している場合
  • QFileInfo オブジェクトが既存のファイルを参照している場合

もし、QFileInfo オブジェクトが指すパスにファイルやディレクトリが存在しない場合、QFileInfo::exists()false を返します。

使い方の例:

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

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

    QString filePath = "my_document.txt"; // 確認したいファイルのパス
    QFileInfo fileInfo(filePath);

    if (fileInfo.exists()) {
        qDebug() << filePath << "は存在します。";
    } else {
        qDebug() << filePath << "は存在しません。";
    }

    QString dirPath = "/home/user/my_directory"; // 確認したいディレクトリのパス
    QFileInfo dirInfo(dirPath);

    if (dirInfo.exists()) {
        qDebug() << dirPath << "は存在します。";
    } else {
        qDebug() << dirPath << "は存在しません。";
    }

    return a.quit();
}

この例では、まず確認したいファイルのパス (filePath) とディレクトリのパス (dirPath) を QString 型で定義しています。次に、それぞれのパスを引数として QFileInfo オブジェクト (fileInfodirInfo) を生成しています。

そして、それぞれの QFileInfo オブジェクトに対して exists() 関数を呼び出し、その戻り値に基づいてファイルやディレクトリが存在するかどうかを qDebug() で出力しています。

このように、QFileInfo::exists() を使うことで、ファイル操作を行う前にファイルやディレクトリの存在を確認し、存在しない場合の処理を適切に行うことができます。これは、プログラムの安定性を高める上で非常に重要な処理となります。



ファイルパスの誤り

  • トラブルシューティング
    • QFileInfo オブジェクトを作成する前に、パスの文字列を qDebug() などで出力して確認する。
    • 絶対パスを使用するように変更してみる。
    • QDir::currentPath() で現在の実行ディレクトリを確認し、相対パスが正しいか検証する。
    • パス区切り文字に / を使用するように統一する (Qt は自動的に適切な区切り文字に変換します)。
  • 原因
    • スペルミスや大文字・小文字の誤り (ファイルシステムによっては区別されます)。
    • 相対パスを使用している場合に、プログラムの実行ディレクトリが想定と異なっている。
    • パス区切り文字 (/\) の誤り (Qt はプラットフォームの違いを吸収しますが、手動で組み立てる場合は注意が必要です)。
  • 問題
    QFileInfo オブジェクトに渡すファイルパスやディレクトリパスが間違っている場合、当然ながら exists()false を返します。

アクセス権の問題

  • トラブルシューティング
    • ファイルシステムの権限設定を確認する (例: chmod, chown コマンドなど)。
    • プログラムを必要な権限で実行してみる (ただし、セキュリティ上のリスクも考慮する必要があります)。
    • QFile::permissions()QDir::permissions() など、Qt のファイル/ディレクトリの権限に関する関数を使用して、プログラム内から権限を確認することも検討できます。
  • 原因
    • ファイルやディレクトリの所有者やグループ、パーミッション設定が適切でない。
    • プログラムが管理者権限で実行されていないために、特定の場所にアクセスできない。
  • 問題
    プログラムを実行しているユーザーに、確認しようとしているファイルやディレクトリへのアクセス権がない場合、exists()true を返す可能性がありますが、その後のファイル操作でエラーが発生します。exists() 自体は存在を確認するだけなので、アクセス権限までは考慮しません。

ファイルやディレクトリの存在タイミング

  • トラブルシューティング
    • ファイル操作の完了を保証するために、適切な同期処理 (シグナルとスロット、ミューテックスなど) を実装する。
    • ファイル操作後に少し遅延を入れてから exists() を呼び出して確認する (ただし、根本的な解決策ではありません)。
  • 原因
    • ファイル作成処理が完了する前に exists() を呼び出している。
    • 別のプロセスやスレッドがファイルやディレクトリを削除した直後に exists() を呼び出している。
  • 問題
    ファイルやディレクトリがプログラムの実行中に作成または削除される場合、exists() を呼び出すタイミングによっては期待通りの結果が得られないことがあります。

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

  • トラブルシューティング
    • リンク先の実体が存在するかどうかを確認したい場合は、QFileInfo::isSymLink() でシンボリックリンクかどうかを確認し、必要であれば QFileInfo::readLink() でリンク先を取得して、さらにそのパスに対して QFileInfo を作成し exists() を呼び出す必要があります。
  • 原因
    exists() はシンボリックリンクの存在を確認するだけで、そのリンクが有効かどうかまではチェックしません。
  • 問題
    QFileInfo オブジェクトがシンボリックリンクを参照している場合、exists() はリンク自体が存在すれば true を返します。リンク先のファイルやディレクトリが存在するかどうかは exists() だけでは判断できません。

ネットワークファイルシステムの問題

  • トラブルシューティング
    • ネットワーク接続を確認する。
    • ネットワークファイルシステムのログを確認する。
    • 可能であれば、ローカルファイルシステムで同様のテストを行ってみる。
  • 原因
    • ネットワークの応答が遅い、または一時的に切断されている。
    • ネットワークファイルシステムの設定に問題がある。
  • 問題
    ネットワーク上のファイルシステム (NFS, SMB など) を扱っている場合、ネットワークの遅延や接続の問題によって、exists() の結果が一時的に正しくなくなることがあります。
  • トラブルシューティング
    • OS のファイルシステムキャッシュをフラッシュする方法を試す (ただし、通常は自動的に管理されるため、手動で操作することは推奨されません)。
    • 時間をおいて再度 exists() を呼び出してみる。
  • 原因
    OS のファイルシステムキャッシュが古い情報を保持している。
  • 問題
    ごく稀なケースとして、ファイルシステムのキャッシュの影響で、exists() の結果が最新の状態を反映しないことがあります。


基本的な存在確認 (ファイルの例)

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>
#include <QFile>

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

    QString filePath = "example.txt";

    // まずはファイルを作成してみる (存在しない場合に備えて)
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        file.write("This is a test file.\n");
        file.close();
        qDebug() << filePath << " を作成しました。";
    } else {
        qDebug() << filePath << " の作成に失敗しました。";
    }

    QFileInfo fileInfo(filePath);

    if (fileInfo.exists()) {
        qDebug() << filePath << " は存在します。";
        qDebug() << "ファイルのサイズ:" << fileInfo.size() << "バイト";
        qDebug() << "最終更新日時:" << fileInfo.lastModified().toString();
    } else {
        qDebug() << filePath << " は存在しません。";
    }

    // 後始末としてファイルを削除する
    QFile::remove(filePath);

    return a.quit();
}

この例では、まず example.txt という名前のファイルを作成し、その後 QFileInfo を使ってそのファイルが存在するかどうかを確認しています。存在する場合は、ファイルサイズと最終更新日時を取得して表示しています。最後に、作成したファイルを削除しています。

基本的な存在確認 (ディレクトリの例)

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

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

    QString dirPath = "example_dir";

    // まずはディレクトリを作成してみる (存在しない場合に備えて)
    QDir dir(dirPath);
    if (dir.mkpath(".")) { // "." は現在のパスにディレクトリを作成することを意味します
        qDebug() << dirPath << " を作成しました。";
    } else {
        qDebug() << dirPath << " の作成に失敗しました。";
    }

    QFileInfo dirInfo(dirPath);

    if (dirInfo.exists()) {
        qDebug() << dirPath << " は存在します。";
        qDebug() << "ディレクトリはディレクトリですか?:" << dirInfo.isDir();
    } else {
        qDebug() << dirPath << " は存在しません。";
    }

    // 後始末としてディレクトリを削除する (中身が空である必要があります)
    QDir().rmdir(dirPath);

    return a.quit();
}

この例では、example_dir という名前のディレクトリを作成し、QFileInfo を使ってそのディレクトリが存在するかどうかを確認しています。存在する場合は、それがディレクトリであるかどうか (isDir()) を確認しています。最後に、作成したディレクトリを削除しています。

シンボリックリンクの存在確認

#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QFile>
#ifdef Q_OS_UNIX
#include <unistd.h> // symlink 関数を使うために必要 (Unix系 OS のみ)
#endif

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

    QString targetFile = "target.txt";
    QString linkFile = "link_to_target.txt";

    // ターゲットファイルを作成
    QFile file(targetFile);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        file.write("This is the target file.\n");
        file.close();
        qDebug() << targetFile << " を作成しました。";
    }

#ifdef Q_OS_UNIX
    // シンボリックリンクを作成 (Unix系 OS のみ)
    if (symlink(targetFile.toUtf8().constData(), linkFile.toUtf8().constData()) == 0) {
        qDebug() << linkFile << " を " << targetFile << " へのシンボリックリンクとして作成しました。";
    } else {
        qDebug() << linkFile << " のシンボリックリンク作成に失敗しました。";
    }

    QFileInfo linkFileInfo(linkFile);
    if (linkFileInfo.exists()) {
        qDebug() << linkFile << " (シンボリックリンク) は存在します。";
        qDebug() << linkFile << " はシンボリックリンクですか?:" << linkFileInfo.isSymLink();
        qDebug() << "リンク先:" << linkFileInfo.readLink();

        QFileInfo targetFileInfoFromLink(linkFileInfo.readLink());
        if (targetFileInfoFromLink.exists()) {
            qDebug() << "リンク先の " << targetFileInfoFromLink.filePath() << " も存在します。";
        } else {
            qDebug() << "リンク先の " << targetFileInfoFromLink.filePath() << " は存在しません。";
        }
    } else {
        qDebug() << linkFile << " は存在しません。";
    }

    // 後始末
    QFile::remove(targetFile);
    QFile::remove(linkFile);
#else
    qDebug() << "この例は Unix 系 OS でのみ動作します。";
#endif

    return a.quit();
}

この例は Unix 系 OS 限定ですが、シンボリックリンクの存在を確認する方法を示しています。exists() はシンボリックリンク自体が存在すれば true を返しますが、リンク先のファイルが存在するかどうかは別の QFileInfo オブジェクトを作成して確認する必要があります。readLink() 関数でリンク先のパスを取得できます。

#include <QCoreApplication>
#include <QFileInfo>
#include <QFile>
#include <QDebug>

void processFile(const QString& filePath) {
    QFileInfo fileInfo(filePath);
    if (fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable()) {
        qDebug() << filePath << " は読み取り可能なファイルです。処理を開始します...";
        // ファイルの読み込みや処理を行うコード
        QFile file(filePath);
        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            QTextStream in(&file);
            QString line;
            while (in.readLine(&line)) {
                qDebug() << "  内容:" << line;
            }
            file.close();
        }
    } else {
        qDebug() << filePath << " は存在しないか、ファイルではないか、または読み取りできません。";
    }
}

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

    QString existingFile = "existing_file.txt";
    QString nonExistingFile = "non_existent_file.txt";

    // existing_file.txt を作成
    QFile createFile(existingFile);
    if (createFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        createFile.write("Hello, world!\n");
        createFile.close();
    }

    processFile(existingFile);
    processFile(nonExistingFile);

    QFile::remove(existingFile);

    return a.quit();
}


QFile::exists() および QDir::exists() (静的関数)

#include <QCoreApplication>
#include <QFile>
#include <QDebug>

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

    QString filePath = "another_example.txt";

    // ファイルを作成
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        file.write("Another test.\n");
        file.close();
    }

    if (QFile::exists(filePath)) {
        qDebug() << filePath << " は QFile::exists() で確認できました。";
    } else {
        qDebug() << filePath << " は QFile::exists() で確認できませんでした。";
    }

    QFile::remove(filePath);

    return a.quit();
}
  • 例 (ディレクトリの確認)
#include <QCoreApplication>
#include <QDir>
#include <QDebug>

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

    QString dirPath = "another_example_dir";

    // ディレクトリを作成
    QDir dir(dirPath);
    dir.mkpath(".");

    if (QDir::exists(dirPath)) {
        qDebug() << dirPath << " は QDir::exists() で確認できました。";
    } else {
        qDebug() << dirPath << " は QDir::exists() で確認できませんでした。";
    }

    QDir().rmdir(dirPath);

    return a.quit();
}
  • 欠点
    ファイルまたはディレクトリの存在確認に特化しており、ファイルサイズ、最終更新日時、パーミッションなどの追加情報を取得するには、別途 QFileInfo オブジェクトを作成する必要があります。
  • 利点
    コードが簡潔になる場合があります。QFileInfo オブジェクトを作成するオーバーヘッドがわずかに少ない可能性があります (通常は無視できる程度ですが)。

QFile::open() または QDir::open() の試行

  • 例 (ファイルの確認 - 開けるかどうか)

#include <QCoreApplication>
#include <QFile>
#include <QDebug>

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

    QString filePath = "yet_another_example.txt";

    // ファイルを作成
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        file.write("Yet another test.\n");
        file.close();
    }

    QFile checkFile(filePath);
    if (checkFile.open(QIODevice::ReadOnly)) {
        qDebug() << filePath << " は開くことができ、存在すると考えられます。";
        checkFile.close();
    } else {
        qDebug() << filePath << " は開くことができませんでした。";
    }

    QFile::remove(filePath);

    return a.quit();
}
  • 例 (ディレクトリの確認 - オープンできるかどうか)
#include <QCoreApplication>
#include <QDir>
#include <QDebug>

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

    QString dirPath = "yet_another_example_dir";
    QDir().mkpath(dirPath);

    QDir checkDir(dirPath);
    if (checkDir.exists()) { // QDir の exists() は QFileInfo::exists() と同様の機能です
        qDebug() << dirPath << " は QDir オブジェクトで存在が確認できました。";
    } else {
        qDebug() << dirPath << " は QDir オブジェクトで存在が確認できませんでした。";
    }

    QDir().rmdir(dirPath);

    return a.quit();
}
  • 欠点
    ファイルの内容を読み書きしたり、ディレクトリの内容を列挙したりする意図がない場合でも、一時的にリソースを開閉する必要があるため、わずかに効率が悪い可能性があります。エラー処理を適切に行う必要があります。ディレクトリの存在確認に関しては、QDir::exists()QFileInfo::exists() と同様の役割を果たします。
  • 利点
    ファイルが存在するだけでなく、指定されたモード (読み取り専用など) でアクセス可能かどうかも同時に確認できます。

プラットフォーム固有のAPIの利用 (非推奨)

  • 例 (POSIX access() を使用する例 - Unix系 OS のみ)
#include <QCoreApplication>
#include <QDebug>
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <QByteArray>
#endif

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

    QString filePath = "platform_specific_example.txt";
    QFile file(filePath);
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    file.close();

#ifdef Q_OS_UNIX
    QByteArray localFilePath = filePath.toLocal8Bit();
    if (access(localFilePath.constData(), F_OK) == 0) {
        qDebug() << filePath << " は POSIX access() で存在が確認できました。";
    } else {
        qDebug() << filePath << " は POSIX access() で存在が確認できませんでした。";
    }
    QFile::remove(filePath);
#else
    qDebug() << "この例は Unix 系 OS でのみ動作します。";
#endif

    return a.quit();
}
  • 欠点
    クロスプラットフォーム性が失われ、コードの移植性が極めて低くなります。Qt が提供する抽象化されたAPIを使用する方が、通常はるかに推奨されます。
  • 利点
    特定のプラットフォームの機能をより細かく制御できる可能性があります。

通常、ファイルやディレクトリの基本的な存在確認には QFileInfo::exists() が最も便利で推奨される方法です。

  • プラットフォーム固有のAPIの直接利用は、移植性の観点から通常は避けるべきです。
  • ファイルのアクセス可能性 (読み書き可能かどうか) を同時に確認したい場合は、QFile::open() を試みる方法も考えられます。
  • より簡潔に存在だけを確認したい場合は、QFile::exists() (ファイル) や QDir::exists() (ディレクトリ) の静的関数が適しています。