閑古鳥

オールドプログラマの日記。プログラミングとか病気(透析)の話とか。

boost::filesystem::path

Boost.Filesystemにはファイル名やディレクトリ名といったパスを表すためのpathクラスがあります。
パスに対する色々な操作を行うことができます。

初期化

文字列を渡して初期化します。

boost::filesystem::path path("C:\\hom\\hom.txt");

パスの出力

operator<<(std::ostream&, path const&) なんかがオーバーロードされているので、出力はそのまま以下のようにできます。

std::cout << path;

文字列を得たい場合はc_strかstringメンバ関数を使います。

std::string const str = path.string();
boost::filesystem::path::value_type const * const ptr = path.c_str();

path::value_typeはWindowsならwchar_tで、OSによって異なるようです。
Filesystem V3 Reference

パスの分解

ディレクトリやファイル名だけ抜き出したり。

std::cout << path.root_name().c_str(); //=> 'C:'
std::cout << path.root_directory();    //=> '\'
std::cout << path.root_path();         //=> "C:\" (ルートディレクトリ root_name() / root_directory())
std::cout << path.relative_path();     //=> "hom\hom.txt"
std::cout << path.parent_path();       //=> 'C:\hom'
std::cout << path.filename();          //=> 'hom.txt'
std::cout << path.stem();              //=> 'hom' (拡張子を除いたファイル名)
std::cout << path.extension();         //=> '.txt' (拡張子)

stemって初めて見ました。ぱっと見わからなかったです。C#(.NET)でいうところのPath.GetFileNameWithoutExtensionですね。

パスの変更

// 拡張子を変更
std::cout << path.replace_extension(".cpp"); //=> 'C:\hom\hom.cpp'
// ファイル名を取り除く
std::cout << path.remove_filename(); //=> 'C:\hom'

パスの連結

パスとパスをoperator/で連結することができます。

boost::filesystem::path dir("C:\\hom");
boost::filesystem::path file("hom.txt");
std::cout << dir / file; //=> 'C:\hom\hom.txt'

自然なような…そうでもないような…。 path /= file で連結することもできます。
また、appendメンバ関数でも同じ事ができるので、わかりにくいよって場合はこちらを使うと良いかも。

相対パス絶対パスに変換する

absolute関数を使うと、相対パス絶対パスにすることができます。
ベースになるパス(baseパス)を指定すると base / relative_path を作ってくれます。
baseを指定しない場合は、カレントディレクトリをbaseとします。

boost::filesystem::path path = "hom.txt";
// baseを指定しないと、カレントディレクトリの下になる
std::cout << boost::filesystem::absolute(path); //=> 'c:\Users\hwada\home\scratch\cpp\test\hom.txt'
// baseを指定する
std::cout << boost::filesystem::absolute(path, "C:\\madoka\\"); //=> 'C:\\madoka\\hom.txt'
// 絶対パスを指定すると無効
path = "C:\\hom\\hom.txt";
std::cout << boost::filesystem::absolute(path, "C:\\madoka\\"); //=> 'C:\\hom\\hom.txt'

path::make_absoluteというメンバ関数もあるよ、とマニュアルにはあったのですが、ソースからは見あたりませんでした。
最後のパターンなんかは例外出しちゃって良い気もします。

パスに対する問い合わせ

パスがファイル名を含んでいるか、などを確認できます。

// パスが空か?
path.empty();
// ルートネームを含んでいるか?
path.has_root_name();
// ルートディレクトリを含んでいるか?
path.has_root_directory();
// ルートパスを含んでいるか?
path.has_root_path();
// !relative_path().empty()
path.has_relative_path();
// 親ディレクトリがあるか?
path.has_parent_path();
// ファイル名を含んでいるか?
path.has_filename();
path.has_stem();
// 拡張子を含んでいるか?
path.has_extension();
// 絶対パス?
path.is_absolute();
// 相対パス?
path.is_relative();

感想

パスの分割や連結にはsplitpath/makepathなどの関数をよっく使っていましたがかなり面倒くさいので、これを使うと楽ができそうです。
まあ最近はShellAPIのPathFindFileNameとかを使う事も多く、こちらは利便性ではあまり変わらなかったりしますが、パスがただの文字列ではなくちゃんとした型になっているのは収まりも良いので、新しいプログラムを書くときには使っていきたいですね。