コード片などをメモします。
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;
}
}
}