トップ 履歴 一覧 ソース 検索 ヘルプ ログイン

C++のメモ

コード片などをメモします。

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;
        }
    }
}

コメント

お名前: コメント: