C++: 変数へのテキストデータの一括読み込み
概要
競技プログラミングやプログラミングコンテストでは標準入力からテキストデータを読み込むところから処理が始まる。
標準入力以外にも,ファイルや文字列などから処理が始まることは多い。そして,読み込んだデータを配列や文字列に格納して,後続の処理につなげる。
C++の標準ライブラリーで,テキストデータの一括読み込み方法を考えたので記す。
実装
先に実装例を以下に示す。GitHubでも公開している。
方法
実装方法について説明する。
実装には,<string>
で定義されているstd::getline()
(N3337 21.4.8.9 Inserters and extractors) を使う。なお,std::basic_string
のメンバー関数にも同名のgetline()
が存在するが,こちらではないので注意する。
改行区切りで文字列をstd::vector
へ格納
1個目のよくある例として,データを改行区切りでstd::vector
に格納する。
これは,以下のようにfor
文やwhile
文でデータストリームがなくなるまでstd::getline()
を繰り返し実行して,取得した1行の文字列をstd::vector
にpush_back
で格納する。
// Read data to line breaked vector.
std::string data = "1\n2\n3\n";
std::stringstream input;
input.str(data);
std::vector<std::string> v;
for (std::string line; std::getline(input, line);) {
v.push_back(line);
}
for (auto& e : v) std::cout << e << "\n";
実に愚直でシンプルな方法だ。
改行込で文字列をstd::string
へ格納
2個目のよくある例として,改行込みでデータ全体を巨大な文字列として変数 (std::string
) に格納する。例えば,XMLやHTML, JSONのように改行に意味のないデータの処理時に使うことがあるだろう。
これもstd::getline()
でこなせる。第3引数に区切り文字を指定できるので,ここに文字列の終端を意味するNULL文字 ('\0'
) を指定することで,テキストデータの末尾まで読み込む。
// Read whole data to string.
input.clear();
input.str(data);
std::string str;
std::getline(input, str, '\0');
std::cout << str;
こちらもかなり手短に実装できた。
文字列 (std::stringstream
) 以外の入力元の処理
今回の例ではプログラム内で処理が簡単に完結するように,std::stringstream
を使って文字列ストリームを入力データとして用意し,std::getline()
の第1引数にこれを指定した。
標準入力やテキストファイルを処理対象とする場合,それぞれ以下のオブジェクトをstd::getline()
の第1引数に指定すればいい。
入力データの形式に依存せずに同じコードでデータを処理できて,オブジェクト指向の便利さを感じた。
結論
C++でテキストデータの一括読み込み方法を整理した。どちらもstd::getline()
で手短に実装できた。
C++の勉強の始めたての頃に,CSVファイルの読み込み方法でPythonに存在したsplit関数がC++のSTLになくて困って,あれこれ四苦八苦した記憶がある。今なら,std::getline()
を駆使して,それほど苦労せずに実装できそうな気がする。時間に余裕があって,知識が身についた今だからいえることかもしれない。
std::getline()
は便利で応用の効く関数なので,文字列処理ではまずこの関数を思い出したい。
なお,実装途中でstd::stringstream
のinput
変数を使いまわそうとすると,データを取得できず行き詰まった。clear()
でinput
に設定されたfailbit
やeofbit
の解除が必要だった。
特によく使う処理に関しては,今回のように整理してサンプルコードも保存して,いつでもすぐに思い出せるようにしたい。
“C++: 変数へのテキストデータの一括読み込み” に対して1件のコメントがあります。