文字列の中で動くストリーム
cin と cout はすでに知っているでしょう。キーボードと画面につながったストリームです。文字列ストリームは同じ考え方ですが、データはその代わりにメモリ上の std::string の中にあります。<< で書き込み、>> で値を取り出すのはコンソールのストリームとまったく同じですが、ターミナルには一切触れません。
このたった 1 つの考え方が、日常的な 2 つの問題を解決します。テキストを型付きの値に解析することと、混在したパーツから整形済み文字列を構築することです。すべては <sstream> ヘッダーに含まれ、3 つの種類があります。
istringstream- 読み取り専用、入力の解析向け。ostringstream- 書き込み専用、出力の構築向け。stringstream- 両方向。
ss.str() は、これまでに書き込まれたすべてをただの string として返します。<< 演算子は cout と同じ int からテキストへの整形を行いました。あなたはそれを出力する代わりに、結果を取得しただけです。
解析: 型付きの値を取り出す
本当の力は >> で現れます。ストリームにテキストを与えると、抽出は各トークンを要求した型へ変換し、その間の空白をスキップします。これは 1 行を複数の型付きフィールドに分解するきれいな方法です。
各 >> は次の空白まで読み取って変換します。"Ada" を string に、"36" を int に、"5.5" を double に。ここで istringstream に注目してください。入力専用の型は、解析だけを行っていることを明確にします。
文字列を数値に変換する(安全に)
よくある作業は、"42" のような 1 つの文字列を int に変えることです。stringstream はそれを行い、さらにテキストが本当に有効な数値だったかどうかを教えてくれます。これは atoi が決してしないことです。
ここでは ss >> value が 123 を読み取り、a で止まって成功します。そのため、重要であれば常に文字列全体を検証してください。堅牢なチェックは、数値を読み取った後に意味のあるものが残っていないことを確認することです。単純な単一値の変換には、std::stoi、std::stod とその仲間(型キャストと一緒に解説)のほうが短く済みます。1 つの文字列が複数の異なる値を運ぶ場合に stringstream を使いましょう。
区切り文字で文字列を分割する
テキストの分割は、最も多く Google 検索される文字列ストリームの作業です。ストリームと std::getline を組み合わせます。getline は改行だけでなく、任意の区切り文字まで読み取れます。
while (getline(...)) のパターンは、ストリームが尽きるまでループします。getline がストリームを返し、何も残っていなくなるとストリームは false と評価されるためです。第 3 引数を外すと、代わりに >> で空白区切りのトークンに分割します。データが使う区切り文字に合わせて選びましょう。
メモリ上で文字列を構築する
逆方向では、ostringstream が sprintf のバッファオーバーフローの危険や、もろい手動連結なしに、整形済み文字列を組み立てます。ログ行、ファイル名、数値とテキストを縫い合わせたメッセージに最適です。
cout で使う <iomanip> の整形 - setw、setfill、setprecision、hex - はすべて文字列ストリームでも同じように動きます。そのため、桁詰めや固定幅、16 進の出力をそのまま string に取得できます。
落とし穴: 再利用の前にリセットする
誰もがはまる罠は、リセットせずにストリームを再利用することです。ストリームがデータの終端に達すると、その eof と fail フラグが固定され、それ以降の >> は何も言わずに何も行いません。エラーもなく、古い値が残るだけです。
stringstream ss("10");
int a, b;
ss >> a; // a = 10、ストリームは終端に -> eof フラグが立つ
ss.str("20"); // 新しいテキストを読み込む...
ss >> b; // 静かに失敗する - エラーフラグがまだ立っており、b は変わらない
エラー状態と内容の両方をクリアする必要があります。正しい順序は、まず clear()、次に str() です。
関連する 2 つの誤り: ss.clear() はフラグをリセットしますが、バッファを空にはしません(それには ss.str("") を使います)。また、<< で追記し続ける stringstream は増え続けるので、汚れたものを各反復で再利用するのではなく、ループの中で新しいものを作りましょう。
次へ: 配列
文字列ストリームは、テキストを型付きの値に、また元に戻すきれいな方法を与えてくれました。多くの場合、CSV を分割しながら埋めた vector のように、その値のリスト全体を扱います。これらの値の固定的なコレクションを格納してインデックス参照するには、言語の最も基本的なコンテナが必要です。次は配列を取り上げます。宣言の仕方、インデックス参照、反復処理、そして C++ で非常に多くの未定義動作を引き起こす範囲外アクセスの回避方法です。
よくある質問
C++ の stringstream とは何ですか?
std::stringstream は、キーボードやファイルではなく string を裏に持つストリームです。<< で書き込み、>> で読み出します。これは cout や cin とまったく同じで、テキストの解析やメモリ上での文字列構築のための標準的なツールになっています。<sstream> ヘッダーに含まれています。
C++ で stringstream を使って文字列を int に変換するには?
テキストをストリームに入れ、数値へ抽出します: stringstream ss("42"); int n; ss >> n;。変換が実際に成功したかを確認するには if (ss)(または if (ss >> n))でチェックします。単純なケースでは std::stoi のほうが短く済みますが、1 つの文字列に複数の異なる値が含まれる場合に stringstream が真価を発揮します。
なぜ stringstream に対して clear() を呼ぶ必要があるのですか?
ストリームがデータの終端に達すると、その eof/fail ビットが立ったままになり、それ以降の >> は何も言わずに何も行わなくなります。同じ stringstream を新しい内容で再利用する場合は、まず ss.clear() でこれらのエラーフラグをリセットし、次に ss.str(newText) で新しいデータを読み込みます。そうしないと読み取りが静かに失敗します。