SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Visual C++ 2017で一足お先にfilesystem

パス文字列の操作

 それではfilesystemのナカミをざっくり紹介しましょうか。なにせ、かなりの数のクラス、関数その他モロモロが定義されており、紹介できるのは主要なものに限りますゴメンナサイ。気になる方は実装の基となった公式ドキュメント:N4100をご一読くださいませ。

 filesystemの中核をなすのがclass path、他のクラスや関数の多くはpathを引数/戻り値としています。

 pathは(ディレクトリを含む)ファイルの在り処を特定する"位置情報"を表し、文字列で表記されるのが一般的でしょうか。なので外見的には(実はその実装も)ディレクトリ/ファイルの操作に特化した特殊な文字列クラスの様相を呈しています。

 pathを使ったサンプルを1つ書いてみました。

list-03 path_member_funcs.cpp
#include <filesystem>
#include <iostream>
//#include <algorithm>

namespace fs = std::experimental::filesystem;

inline void put(const std::string& str, const fs::path& p) 
  { std::cout << str << " :\t" << p << std::endl; }

inline void put(const std::string& str, bool b) 
  { std::cout << str << " :\t" << std::boolalpha << b << std::endl; }

int main() {
  using namespace std;
  wcout.imbue(locale("ja"));
  fs::path p = R"(d:/初段\中段\最終.目的.地点)";

  cout  << p << endl; // ostreamにも
  wcout << p << endl; // wostreamにも出力できる

  put("is_absolute      ",  p.is_absolute());    // 絶対パスか?
  put("is_relative      ",  p.is_relative());    // 相対パスか?
  put("root_name        ",  p.root_name());      // ルート名
  put("root_directory   ",  p.root_directory()); // ルート・ディレクトリ名
  put("relative_path    ",  p.relative_path());  // ルートを除いた相対パス
  put("parent_path      ",  p.parent_path());    // 親ディレクトリ
  put("filename         ",  p.filename());       // ファイル名
  put("stem             ",  p.stem());           // 拡張子を除いたファイル名
  put("extension        ",  p.extension());      // 拡張子
  put("empty            ",  p.empty());          // 空パスか?
  put("replace_filename ",  p.replace_filename("the.end")); // ファイル名変更
  put("replace_extention",  p.replace_extension(".final")); // 拡張子変更
  put("remove_filename  ",  p.remove_filename()); // ファイル名削除
  put("/ \"末尾/追加\"    ", p / "末尾/追加");    // 連結

}

 ディレクトリを区切るセパレータ文字は/または\(混在もOK、Linuxだと/のみ)で、path内に取り込まれた時点で\に変換されるみたいです。operator/、operator/=でpathが連結されるのはシャレが効いてます。

 サンプルには示していませんが、has_rootname()やhas_extension()などの各種bool has_XXX() constが定義されていて、XXXがあるか否かを判定してくれます。

 加えてpathをセパレータでぶつ切りにしてくれるbegin()/end()を持っています。なので、

list-04 path_member_funcs.cpp
#include <iostream>
#include <filesystem>

int main() {
  using namespace std;
  namespace fs = std::experimental::filesystem;

  fs::path p = R"(d:\初段\中段\最終.目的.地点)";
  for ( auto iter = p.begin(); iter != p.end(); ++iter ) {
      cout << "[" << *iter << "]\n"; // *iter は path を返す
  }
#if 0
  // これでもOK
  for (auto item : fs::path(R"(d:\初段\中段\最終.目的.地点)")) {
    cout << "[" << item << "]\n";
  }
#endif
}

 これを実行するとルート名、ルートパス、ファイル名……に区切られて、こんな結果が得られます。これ便利。

 そんなわけでpathはいわば"パス文字列操作クラス"であり、実在するディレクトリ/ファイルを操作できるものではありません。実際にディレクトリを作ったりファイルを削除したりといった実在するディレクトリに対する操作は、pathとは別にグローバル関数で提供されています。主要な関数を列挙します。

  • path current_path()
    カレント・ディレクトリを返します。
  • void current_path(const path& p)
    pをカレント・ディレクトリに設定します。
  • path temp_directory_path()
    テンポラリ・ディレクトリを返します。環境変数TMPに設定されたディレクトリが返ってくるみたい。
  • path absolute(const path& p, const path& base = current_path())
    相対パスpを、baseを起点とした絶対パスに変換して返します。
  • path canonical(const path& p, const path& base = current_path())
    "canonical"は正統とか生準といった意味。baseを起点とし、pから.および..を含まないパス表現に変換して返します。例えば"d:\abc\.\def\..\ghi"を"d:\abc\ghi"のように。
  • bool exists(const path& p)
    pが示すディレクトリ/ファイルが存在すればtrueを返します。
  • uintmax_t file_size(const path& p)
    pが示すファイルのサイズ(バイト数)を返します。uintmax_tは最大の符号なし整数型:unsigned long longのtypedef。
  • void resize_file(const path& p, uintmax_t new_size)
    pが示すファイルのサイズをnew_sizeにします。
  • bool is_directory(const path& p)
    pがディレクトリであればtrueを返します。
  • bool is_regular_file(const path& p)
    pが(通常の)ファイルであればtrueを返します。
  • file_time_type last_write_time(const path& p)
    pが示すディレクトリ/ファイルの最終更新時刻を返します。file_time_typeはstd::chrono::time_point<std::chrono::system_clock>のtypedefでしたから、次のように実時間に変換できます。
list-05
fs::file_time_type ft = fs::last_write_time("filesystem.cpp");
time_t tm = std::chrono::system_clock::to_time_t(ft);
// または tm = std::chrono::duration_cast<std::chrono::seconds>(ft.time_since_epoch()).count()
  • void last_write_time(const path& p, file_time_type new_time)
    pが示すディレクトリ/ファイルの最終更新時刻をnew_timeに設定します。
  • bool create_directory(const path& p)
    pで示されるディレクトリを作成します。
  • bool create_directories(const path& p)
    これもpで示されるディレクトリを作成するのですが、create_directories("foo/bar/fred")なんてときに必要に応じてディレクトリfoo, bar ...を作り、ディレクトリを掘り進んでくれます。便利。
  • bool remove(const path& p)
    pで示されたディレクトリ/ファイルを削除します。
  • uintmax_t remove_all(const path& p)
    pで示されたディレクトリ/ファイルを削除します。pがディレクトリであったとき、その配下にあるディレクトリ/ファイルも再帰的に削除してくれます。
  • void copy(const path& from, const path& to, copy_oprions options)
    fromからtoへディレクトリ/ファイルをコピーします。ディレクトリ階層の再帰的なコピーや、toが既存であったときの動作などは第三引数で指定します。

次のページ
ディレクトリの再帰的な扱い

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/10076 2017/05/16 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー