コード片などをメモします。
C++関連ウェブサイト
リンク | 内容 |
---|---|
C++ - Standards | 各バージョンのC++言語仕様のドラフト(PDF)を提供。 |
C++国際標準規格 | cpprefjp内のページ。上のサイトにあるものより新しいドラフトへのリンクがある。 |
C++ Core Guidelines | ガイドライン |
cpprefjp | 日本語による標準ライブラリのリファレンスなどを提供。 |
cppreference.com | 標準ライブラリのリファレンス。日本語ページもある。 |
cplusplus.com | 標準ライブラリのリファレンス。更新が止まっているのかな。C++14以降の情報はなさそう。 |
コード片
文字列操作
置換
std::stringクラスのメンバ関数replaceはオーバーロードの数が多くて圧倒されてしまう。このメンバ関数は、元の文字列の「指定位置の文字列」を別の文字列に置換するのに使える。置換前後の文字列長は変わってもよい。
- string& replace(size_type pos1, size_type n1, const string& str);
- 対象文字列のpos1から長さn1の部分を、strに置換する。
// "hello world" ---> "hell(^^)(^^)(^^)orld" std::string s = "hello world"; s.replace(4, 3, "(^^)(^^)(^^)");
- string& replace(size_type pos, size_type n1, size_type n2, charT c);
- 対象文字列のpos1から長さn1の部分を、n2文字のcに置換する。
// "hello world" ---> "hellZZZZZorld" std::string s = "hello world"; s.replace(4, 3, 5, 'Z');
文字列中の特定の文字を別の文字に置き換えるのケースでは、<algorithm>にあるstd::replaceを使える。
// "hello world" ---> "heLLo worLd" std::string s = "hello world"; std::replace(s.begin(), s.end(), 'l', 'L');
dirname 相当
find_last_of() と substr() を使ってみる。
std::string dirname(const std::string& path) { return path.substr(0, path.find_last_of('/')); }
短いが、入力文字列によってはdirnameらしくない動きになってしまう。
dirname("/dir/dir/file"); //=> "/dir/dir" dirname("/dir/dir/file/"); //=> "/dir/dir/file" dirname("/dir/dir//file"); //=> "/dir/dir/" dirname("file"); //=> "file" dirname("/file"); //=> "" dirname(""); //=> ""
basename 相当
同じく find_last_of() と substr() を使ってみる。
std::string basename(const std::string& path) { return path.substr(path.find_last_of('/') + 1); }
std::string::npos == -1 らしいのでこれでいいみたい。
basename("/dir/dir/file"); //=> "file" basename("/dir/dir/file/"); //=> "" basename("file"); //=> "file" basename(""); //=> ""
行末の空白削除
find_last_not_of() を使えばよさそう。
std::string trim_right( const std::string& line, const std::string& blank = " \t\r\n") { return line.substr(0, line.find_last_not_of(blank) + 1); }
trim_right(" str ing\t \r\n"); //=> " str ing" trim_right(" str ing"); //=> " str ing" trim_right("\n"); //=> "" trim_right(""); //=> ""
行頭の空白削除
find_first_not_of() を使う。行末ほど簡単にならなかった。substr() の最初の引数に npos を渡せればいいのに。
std::string trim_left( const std::string& line, const std::string& blank = " \t\r\n") { std::string::size_type begin = line.find_first_not_of(blank); if (begin == line.npos) return ""; return line.substr(begin); }
trim_left(" \t str ing "); //=> "str ing " trim_left("str ing "); //=> "str ing " trim_left("\n"); //=> "" trim_left(""); //=> ""
ストリーム
行単位でストリームから読み込む
std::getline()を使うのがよい。エラーとEOFの検出は、std::getline()の戻り値をチェックすることで行う。
std::deque<std::string> read_lines(std::istream& in) { std::deque<std::string> result; for (std::string line; std::getline(in, line); /**/) { result.push_back(line); } return result; }
ストリームの内容一度に全部読み込む
ストリームからstd::istreambuf_iteratorオブジェクトを作って、それをstd::stringのコンストラクタに渡すのがよさそう。
std::string read_stream(std::istream& in) { return std::string( std::istreambuf_iterator<char>{in}, std::istreambuf_iterator<char>{}); }
ファイルサイズの取得
std::basic_istreamクラスのメンバ関数tellg()でストリームの現在位置を得られるのでそれを使う。
std::ifstreamクラスのコンストラクタで指定するstd::ios_base::openmodeには「ストリームの最後にシークする」というateという定数があるのでそれも使ってみる。
std::int64_t file_size(const std::string& filename) { std::ifstream ifs( filename, std::ios_base::in | std::ios_base::binary | std::ios_base::ate); return static_cast<std::int64_t>(ifs.tellg()); }
ファイルコピー
std::basic_ostreamクラスにはstd::basic_streambufを引数にとるoperator<<()があるのでそれを使うとよさそう。
あと、std::basic_iosクラスのメンバ関数exceptions()を呼ぶとエラー時に例外を投げてくれるようになる。
void copy(const std::string& src, const std::string& dst) { using std::ios_base; std::ifstream ifs; ifs.exceptions(ios_base::badbit | ios_base::failbit); ifs.open(src, ios_base::in | ios_base::binary); std::ofstream ofs; ofs.exceptions(ios_base::badbit | ios_base::failbit); ofs.open(dst, ios_base::out | ios_base::binary); ofs << ifs.rdbuf(); }
連想コンテナ
条件を満たす要素の削除
連想コンテナに対してはstd::remove_if()を用いるイディオムは使えないので、自分でループを書く。後置インクリメントを使う必要がある。Effective STLの第9項「消去オプションは注意して選択しよう」に説明がある。
void erase_if_value_is( std::map<int, std::string>& m, const std::string& condition) { for (std::map<int, std::string>::iterator i = m.begin(), end = m.end(); i != end; /* empty */) { if (i->second == condition) { m.erase(i++); } else { ++i; } } }