LoginSignup
5
1

More than 5 years have passed since last update.

getlineのデリミタを文字列に対応させる

Last updated at Posted at 2016-04-02

標準にはありそうでなかった、getlineの第三引数がstd::stringのオーバーロードを書いたので載せるだけ。実際にはstd空間に追加しちゃだめだけど…
自分で書いてる人も多いと思うけどコピペすれば使えるよ。
これを使えば文字列を区切り文字に使えるので、単語を区切り文字にしたり、\r\nを区切り文字にしたりということができるようになるよ。

// for example..
std::istringstream iss("world wide word");
std::string delim = " wo";

std::string str;
getline(iss, str, delim);
std::cout << str << std::endl; // outputs "world wide"

注意点は全て標準のstd::getlineと同じつもりですが、思い違いがあったり不足があったりするかもしれません。
 ・出力であるoutputは関数の最初にクリアされる。
 ・eof, bad, failのいずれかのフラグが立っていると関数は失敗する。その際、ストリームにfailフラグが立つ。
 ・ストリームの中にstd::string::max_size()と同じかそれより多くの文字が入っていると関数は失敗する。その際、ストリームにfailフラグが立つ。
 ・ストリームの先頭がデリミタであった場合を除き、一文字も出力がなかったとき関数は失敗したとみなされる。その際、ストリームにfailフラグが立つ。
 ・デリミタが見つからなかった場合、ストリームにeofフラグが立つ。
 ・デリミタは出力されない。

以下実装。本当はistreamの右辺値参照版のオーバーロードも作るべきです。
主な処理はdo-while文の中身だけで、ラムダ式の中でデリミタの判定をしているだけなので関数の長さほどやってることは多くないです。

#include <istream>
#include <string>
#include <exception>

template< class Char, class Traits, class Alloc >
std::basic_istream< Char, Traits >& getline(
    std::basic_istream< Char, Traits >& is,
    std::basic_string< Char, Traits, Alloc >& output,
    const std::basic_string< Char, Traits, Alloc >& delim)
{
  if (!delim.size()) { return std::getline(is, output); }
  output.erase();
  is.peek();
  using uint_enough =
    typename std::basic_string< Char, Traits, Alloc >::size_type;
  if (!is.good()
      || static_cast< uint_enough >(is.rdbuf()->in_avail())
          == output.max_size())
  { // output.max_size() characters have been stored,
    // in which case getline sets failbit and returns.
    is.clear(std::ios::failbit | is.rdstate());
    return (is);
  }

  auto is_to_continue = [&]
  { // lambda
    for (std::size_t i = 1; i < delim.size() && is.good(); ++i)
    {
      Char c = is.get();
      output.push_back(c);
      if (!Traits::eq(c, delim[i])) { return true; }
    }
    output.resize(output.size() - delim.size());
    return false;
  };

  do
  { // extracts the input
    std::basic_string< Char, Traits, Alloc > buf;
    std::getline(is, buf, delim.front());
    output += buf;
    if (!is.good()) { break; }  // no delim found or some errors occured.
    output.push_back(delim.front());
  } while (is_to_continue());
  return (is);
}
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1