C++でプログラムを開発している際、ログの出力先やデータの保存場所として「新しいディレクトリを作成したい」という場面は頻繁に発生します。
かつてC++では、OSごとに異なるAPI(WindowsならWin32 API、LinuxならPOSIXなど)を使い分ける必要がありましたが、C++17以降は標準ライブラリの「filesystem」を使用することで、プラットフォームに依存しない共通のコードでディレクトリ操作が可能になりました。
本記事では、ディレクトリ作成の基本となるcreate_directoryと、階層構造を一気に作成できるcreate_directoriesの使い方を、初心者の方でも理解できるように詳しく解説します。
エラーハンドリングやパーミッションの設定など、実務で役立つテクニックも併せて紹介します。
C++におけるディレクトリ作成の標準化
C++17というバージョンが登場するまで、標準ライブラリには「ファイルシステム」を操作する機能が含まれていませんでした。
そのため、開発者はプロジェクトごとに外部ライブラリのBoost.Filesystemを導入したり、OS固有の関数を呼び出すためのラッパーを作成したりしていました。
しかし、現代のC++ではstd::filesystemという名前空間の下に、ディレクトリ作成、削除、移動、存在確認といった機能が統合されています。
これにより、一度書いたコードがWindowsでもLinuxでもMacでもそのまま動作するという、非常に高いポータビリティを実現しています。

開発環境の準備
std::filesystemを使用するには、比較的新しいコンパイラが必要です。
以下の条件を満たしているか確認してください。
| 項目 | 必要なバージョン/設定 |
|---|---|
| 言語規格 | C++17 以上 (C++20/23推奨) |
| ヘッダー | #include <filesystem> |
| 名前空間 | std::filesystem |
単一ディレクトリの作成:create_directory
最も基本的な関数はstd::filesystem::create_directoryです。
この関数は、指定されたパスに新しいディレクトリを一つ作成します。

基本的なサンプルコード
まずは、カレントディレクトリに「logs」という名前のフォルダを作成する単純なプログラムを見てみましょう。
#include <iostream>
#include <filesystem> // std::filesystemを使用するために必要
namespace fs = std::filesystem;
int main() {
// 作成したいディレクトリのパスを指定
fs::path dir_path = "logs";
try {
// ディレクトリを作成
// 戻り値は作成に成功した場合はtrue、既に存在していた場合はfalse
if (fs::create_directory(dir_path)) {
std::cout << "ディレクトリ '" << dir_path << "' を作成しました。" << std::endl;
} else {
std::cout << "ディレクトリ '" << dir_path << "' は既に存在するか、作成できませんでした。" << std::endl;
}
} catch (const fs::filesystem_error& e) {
// 例外が発生した場合(親ディレクトリが存在しない場合など)
std::cerr << "エラー: " << e.what() << std::endl;
}
return 0;
}
ディレクトリ 'logs' を作成しました。
create_directoryの動作仕様
create_directoryにはいくつかの重要なルールがあります。
1. 親ディレクトリが必須
create_directory("data/backup")を実行しようとした際、もし「data」というディレクトリがまだ存在していない場合、この関数は失敗します。
つまり、一度に一段階の階層しか作成できません。
2. 戻り値の意味
この関数はbool値を返します。
trueが返ってきた場合は「新しく作成された」ことを意味し、falseが返ってきた場合は「既に存在していた」ことを意味します。
ディレクトリが既に存在することはエラー扱いにならないという点に注意してください。
3. 属性の継承
新しく作成されたディレクトリの所有権やパーミッションは、実行ユーザーの権限やOSのデフォルト設定に従います。
中間ディレクトリも一気に作成:create_directories
開発現場では、「data/2026/01/report」のように、深い階層のディレクトリを一度に作成したいことがよくあります。
このとき、create_directoryを何度も呼ぶのは手間がかかります。
そこで登場するのがstd::filesystem::create_directories(末尾にsがつく複数形)です。

再帰作成のサンプルコード
以下のコードは、途中のフォルダが存在しなくても、指定された末端のフォルダまでを芋づる式に作成します。
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
// 深い階層のパスを指定
fs::path deep_path = "data/archives/2026/internal";
// std::error_codeを使用して例外を投げない形式で呼び出す
std::error_code ec;
if (fs::create_directories(deep_path, ec)) {
std::cout << "深い階層のディレクトリをすべて作成しました。" << std::endl;
} else {
if (ec) {
// エラーが発生した場合
std::cerr << "エラー発生: " << ec.message() << std::endl;
} else {
// エラーはないが、既に存在していた場合
std::cout << "ディレクトリは既に存在しています。" << std::endl;
}
}
return 0;
}
深い階層のディレクトリをすべて作成しました。
create_directoriesの利点と注意点
create_directoriesは非常に強力ですが、以下の特性を理解しておく必要があります。
- 自動的な親階層作成:指定されたパスの中で存在しないディレクトリをすべて順番に作成してくれます。
- 安全性:既に一部の階層が存在していても問題ありません。存在しない部分だけが補完されます。
- パスの妥当性:パスが非常に長すぎる場合や、無効な文字が含まれている場合はOS側で拒否され、エラーコードが返されます。
エラーハンドリングの2つの手法
C++のファイルシステムライブラリでは、エラーを報告する方法が2種類用意されています。
どちらを使うべきかは、プログラムの設計方針によります。
例外(Exception)を使用する方法
引数にパスだけを渡す形式です。
ファイルシステムの問題を「致命的な異常」として扱いたい場合に適しています。
try {
fs::create_directory("my_dir");
} catch (const fs::filesystem_error& e) {
// ディスクフル、書き込み権限不足などで発生
std::cerr << e.what() << std::endl;
}
エラーコード(std::error_code)を使用する方法
第二引数にstd::error_codeの変数を渡す形式です。
例外を投げないため、パフォーマンスを重視する場合や、エラーが頻発することが予想される場合に便利です。
std::error_code ec;
fs::create_directory("my_dir", ec);
if (ec) {
// ec.value() でエラー番号、ec.message() で詳細メッセージを取得可能
std::cout << "失敗: " << ec.message() << std::endl;
}
ディレクトリの状態を確認する関連関数
ディレクトリを作成する前に、あるいは作成した後に、そのパスが本当にディレクトリなのか、あるいはファイルなのかを確認したいことがよくあります。

存在確認と属性確認
以下の関数を組み合わせることで、より堅牢なプログラムを作成できます。
| 関数名 | 説明 |
|---|---|
fs::exists(path) | 指定したパス(ファイルまたはディレクトリ)が存在するか |
fs::is_directory(path) | 指定したパスがディレクトリかどうか |
fs::is_regular_file(path) | 指定したパスが通常のファイルかどうか |
実践的なチェックフロー
fs::path p = "config_folder";
if (fs::exists(p)) {
if (fs::is_directory(p)) {
std::cout << "既にディレクトリとして存在します。" << std::endl;
} else {
std::cout << "同名のファイルが存在するため、ディレクトリを作成できません!" << std::endl;
}
} else {
fs::create_directory(p);
}
このように、作成前にexistsやis_directoryを併用することで、予期せぬ上書きやエラーを未然に防ぐことができます。
パーミッション(権限)の設定
LinuxやmacOSなどの環境では、ディレクトリ作成時に「誰が読み書きできるか」という権限設定が重要になります。
パーミッション指定付きの作成
create_directoryの第二引数(または第三引数)に既存のディレクトリを指定すると、そのディレクトリの権限をコピーして新しいディレクトリを作成できます。
// 既存のフォルダ "template_dir" の権限を模倣して "new_dir" を作成
fs::create_directory("new_dir", "template_dir");
また、std::filesystem::permissions関数を使用することで、作成後に詳細なビット設定を行うことも可能です。
// 作成後に権限を変更する例
fs::create_directory("secure_data");
fs::permissions("secure_data",
fs::perms::owner_all | fs::perms::group_read,
fs::perm_options::replace);
よくあるトラブルと解決策
ディレクトリ作成時に陥りやすい罠とその対策をまとめました。
1. パス区切り文字の問題
Windowsではバックスラッシュ\、Linuxではスラッシュ/が使われますが、std::filesystem::pathクラスを通せば、C++が自動的にOSに合わせた変換を行ってくれます。
コード内ではスラッシュ/で記述するのが最も安全でポータブルです。
2. 書き込み権限(管理者権限)
システムフォルダ(WindowsのProgram Files内など)にディレクトリを作成しようとすると、プログラムが管理者権限で実行されていない限り、必ず失敗します。
この場合はエラーコード「Permission denied」が返されます。
3. 同名ファイルの存在
「data」という名前のファイルが既に存在する場合、同じ「data」という名前のディレクトリを作成することはできません。
これはOSレベルの制約です。
まとめ
C++17で導入されたstd::filesystemライブラリにより、ディレクトリ作成は驚くほど簡単かつ安全になりました。
- 一段階の作成ならcreate_directoryを使用する。
- 複数階層の作成ならcreate_directoriesを使用する。
- エラーハンドリングには、例外処理または
std::error_codeのいずれかを選択する。 - 作成前には
existsやis_directoryで状態を確認するのがベストプラクティス。
これらの機能を使いこなすことで、OSの違いを意識することなく、堅牢なファイル操作機能をC++アプリケーションに組み込むことができます。
ぜひ、あなたのプロジェクトでも最新のファイルシステムAPIを活用してみてください。
