【C++】ファイルの基本操作

2019年2月19日

C++でのWindows環境のファイル操作はWinAPIを使うのがデフォルトでしたがC++17からはfilesystemのライブラリが標準で組み込まれました。(標準なんでLinuxやMacでも使えるのかな?)

これによりファイルのコピーや移動、削除、ファイルパスの取り扱い操作などが簡単にできます。今回はこのfilesystemでよく使いそうなメソッドのみを紹介していきたいと思います。

環境はWindows10 Visual Studioのデバッグモードで、デフォルトの実行階層はソースが配置されている場所となります。

もしVisual Studioでfilesystemのライブラリが使用できない場合こちらを参考にC++17を有効化しておきましょう。



アプリケーションの実行階層を取得

アプリケーションの実行階層を取得するにはcurrent_path関数を使用します。

#include <filesystem>
#include <iostream>
void main() {
	std::filesystem::path path = std::filesystem::current_path();
	std::cout << path.string() << std::endl;
	system("pause");
}
実行結果

C:\Users\ユーザ名\source\repos\Test\Test

※system(“pause”);は実行結果が見やすいように入れています。

相対パスを絶対パスに変換

相対パスを絶対パスに変換するにはabsolute関数を使用します。

#include <filesystem>
#include <iostream>
void main() {
	std::filesystem::path result = std::filesystem::absolute("./test.txt");
	std::cout << result.string() << std::endl;
	system("pause");
}
実行結果

C:\Users\ユーザ名\source\repos\Test\Test\test.txt

指定階層の親階層を取得する

#include <filesystem>
#include <iostream>
void main() {
	std::filesystem::path path = std::filesystem::current_path();
	std::filesystem::path parent = path.parent_path();
	std::cout << std::filesystem::absolute(parent).string() << std::endl;
	system("pause");
}
実行結果

C:\Users\ユーザー名\source\repos\Test

ファイル、フォルダなどが存在するか

指定パスにエンティティが存在するかどうかはexists関数を使用します。

実行フォルダにはファイルを作成して試してみます。

#include <filesystem>
#include <iostream>
void main() {
	bool isExists = std::filesystem::exists("./test.txt");
	std::cout << (isExists ? "true" : "false") << std::endl;
	system("pause");
}
実行結果

true

ファイルかフォルダかを判定する

is~関数で、そのエンティティが何なのかを判定できます。

実行フォルダには同じくファイルを配置。

is_directoryでフォルダかどうかを判定できます。

#include <filesystem>
#include <iostream>
void main() {
	bool isDirectory = std::filesystem::is_directory("./test.txt");
	std::cout << (isDirectory ? "true" : "false") << std::endl;
	system("pause");
}

false

is_regular_fileでファイルかどうかを判定できます。

#include <filesystem>
#include <iostream>
void main() {
	bool isFile = std::filesystem::is_regular_file("./test.txt");
	std::cout << (isFile ? "true" : "false") << std::endl;
	system("pause");
}

true

※ちなみにis_symlinkなどのシンボリックリンクを判定するものも存在します。

シンボリックリンク以外もありますので、詳細を知りたい方はこちらのサイトで勉強するのが得策です。

ファイル、フォルダなどをコピーする

ファイルやフォルダをコピーする場合はcopy(コピー元のパス, コピー先のパス,[コピーオプション])関数を利用します。

test.txtを作ってコピーしてみます。

下記のプログラムを実行します。

#include <filesystem>
	std::filesystem::copy("./test.txt", "./test2.txt");
}
実行結果

もしフォルダの中を再帰的に走査してコピーしたい場合はコピーオプションにrecursiveを指定します。windowsでフォルダコピーしたときと同じような動作をしますので、こちらのほうが一般的でしょう。

#include <filesystem>
	std::filesystem::copy("./test", "./test2",std::filesystem::copy_options::recursive);
}

ファイル、フォルダなどを移動、または名前を変更

ファイルの移動、ファイル名の変更などはrename関数を使用します。

まずはファイル名を変更してみます。実行フォルダにtest2.txtファイルを配置しました。

test2.txtをtest3.txtにファイル名を変更してみます。

#include <filesystem>
void main() {
	//test2.txtをtest3.txtに変更
	std::filesystem::rename("./test2.txt", "./test3.txt");
}
実行結果

次はファイルを移動してみます。実行フォルダにtest3.txtファイルとtestフォルダを配置し、test3.txtをtestフォルダに移動してみます。

#include <filesystem>
void main() {
	//test3.txtをtestフォルダに移動
	std::filesystem::rename("./test3.txt", "./test/test3.txt");
}
実行結果

ファイル、フォルダを削除する

ファイル、フォルダを削除するにはremove関数を使用します。フォルダの中にファイルなどが存在する場合はエラーが発生します。

フォルダの中身も全部削除する場合はremove_all関数を使用します。

サンプルではtest.txtファイルとtestフォルダを削除してみます。(フォルダの中にはファイルが存在している状態です。)

#include <filesystem>
void main() {
	//ファイル削除
	std::filesystem::remove("./test.txt");

	//ファイル削除(全階層)
	std::filesystem::remove_all("./test");
}
実行結果

どちらも削除されました。



フォルダ作成

フォルダ作成はcreate_directory関数を使用します。

指定階層まで全フォルダを作成する場合はcreate_directories関数を使用します。

#include <filesystem>
void main() {
	//フォルダ作成
	std::filesystem::create_directory("./test3");

	//フォルダ作成(全階層)
	std::filesystem::create_directories("./test3/inner/inin");
}
実行結果

フォルダ3が作成されました。create_directoriesによりtest3にはさらにinnerフォルダ、ininフォルダが作成されているはずです。

フォルダの中身を取得する

フォルダの中身を取得するにはdirectory_iteratorを使用します。

全階層取得する場合はrecursive_directory_iteratorを使用します。

サンプルコードを試す際に使用するファイル構成は下記のイメージの通りとします。

#include <filesystem>
#include <iostream>

void main() {
	std::filesystem::directory_iterator e = std::filesystem::directory_iterator("./test");
	for (auto f : e) {
		std::cout << f.path().string() << std::endl;
	}
	system("pause");
}
実行結果

./test\test.txt
./test\test2.txt
./test\test3.txt
./test\testin

ついでにrecursive_directory_iteratorを使用したほうも試しておきましょう。

#include <filesystem>
#include <iostream>

void main() {
	std::filesystem::recursive_directory_iterator e = std::filesystem::recursive_directory_iterator("./test");
	for (auto f : e) {
		std::cout << f.path().string() << std::endl;
	}
	system("pause");
}
実行結果

./test\test.txt
./test\test2.txt
./test\test3.txt
./test\testin
./test\testin\test3.txt

しっかりフォルダの中のフォルダの中まで取得できていますね。

C++

Posted by nompor