C++編【標準ライブラリ】 第29章 文字列ストリーム

先頭へ戻る

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

文字列ストリーム

標準ライブラリのストリームクラスを使って、文字列に対する入出力を行うことができます。 これは、C言語における sprintf関数(→リファレンス)や sscanf関数(→リファレンス)の代替になる機能です。

文字列ストリームのクラス群は、次のようになっています。

文字列ストリームのクラス構造

読み取り用の basic_istringstream<>、書き込み用の basic_ostringstream<>、 読み書き両用の basic_stringstream<> があります。 また、それぞれのワイド文字版として、basic_wistringstream<>basic_wostringstream<>basic_wstringstream<> があります。
<> が付いていることから分かるように、これらはすべてクラステンプレートです。

標準入出力ストリームやファイルストリームと同じく、これらのクラスに対する typedef が定義されています。

namespace std {

	template <typename T, typename Traits = char_traits<T> >
	class basic_istringstream : public std::basic_istream<T, Traits> {
	};

	template <typename T, typename Traits = char_traits<T> >
	class basic_ostringstream : public std::basic_ostream<T, Traits> {
	};

	template <typename T, typename Traits = char_traits<T> >
	class basic_stringstream : public std::basic_iostream<T, Traits> {
	};


	typedef basic_istringstream<char>    istringstream;
	typedef basic_istringstream<wchar_t> wistringstream;
	
	typedef basic_ostringstream<char>    ostringstream;
	typedef basic_ostringstream<wchar_t> wostringstream;
	
	typedef basic_stringstream<char>     stringstream;
	typedef basic_stringstream<wchar_t>  wstringstream;
}

文字列ストリームの各クラステンプレートや typedef された定義は、 sstream という名前の標準ヘッダで定義されています。

なお、文字列ストリームのクラス(クラステンプレート)はそれぞれ、標準入出力ストリームのクラスから派生する形で定義されていますから、 第27章で紹介した内容はそのまま当てはまります。

文字列ストリームへの書き込み (ostringstream)

文字列ストリームを使って、文字列へ書き込みを行うには、 まず ostingstream のオブジェクトを生成し、そこへ <<演算子などの方法でデータを流し込んでいきます。 そして、最終的に、strメンバ関数を呼び出すと、完成した文字列を basic_string型で受け取ることができます。

つまり、文字列ストリームは、ストリーム自身がバッファを持っており、そこへ書き込みを行い、 任意のタイミングで、バッファの内容を返すという使い方になっています。 メモリ管理の責任が文字列ストリーム側にあるため、バッファオーバーフローを起こすことも、 メモリ解放を忘れてしまうこともなく安全であると言えます。

#include <iostream>
#include <sstream>

int main()
{
	std::ostringstream oss;

	oss << "test" << 123 << std::endl;

	std::cout << oss.str() << std::endl;
}

実行結果:

test123

このサンプルプログラムの場合、文字列ストリームへの書き込み時と、その結果を標準出力ストリームへ書き込む時の2か所で、 std::endlマニピュレータを使っているため、改行が2度行われてしまいます。 改行無しでフラッシュだけを行う std::flushマニピュレータを使えば、余分な改行を無くすことができます。
マニピュレータについては、第30章で改めて解説します。

#include <iostream>
#include <sstream>

int main()
{
	std::ostringstream oss;

	oss << "test" << 123 << std::flush;

	std::cout << oss.str() << std::endl;
}

実行結果:

test123

引数の無い strメンバ関数は、その時点でのバッファの内容のコピーを basic_string型で返します。 また、引数に basic_string型の const参照を取る strメンバ関数もあり、こちらはバッファの内容を直接的に書き換えます。 これは、バッファの内容をクリアしたいときに利用できる方法です。

oss.str("");  // バッファをクリア

ちなみに、clearメンバ関数もありますが、これは第27章で取り上げているように、 ストリームの状態フラグをクリアするための関数なので、バッファをクリアするものではありません。


また、既に文字列が存在している場合、その文字列を初期値として与えることも可能です。

std::string s("test");
std::ostringstream oss(s);

この場合、文字列ストリームのバッファには、与えた文字列のコピーが保持されます。 そのため、上の例で言えば、後で変数 s の内容を変更したとしても、文字列ストリームに与えた文字列には影響しません。

ただし、この後、<<演算子を使うなどして書き込みを行うと、文字列が上書きされてしまいます。 これは、書き込み位置がバッファの先頭になっていることが原因なので、 ファイルストリームのランダムアクセスのところで説明したように、 seekpメンバ関数を使って書き込み位置を変更してやれば解消します。

#include <iostream>
#include <sstream>

int main()
{
	std::string s("test");
	std::ostringstream oss(s);
	
	oss.seekp(0, std::ios_base::end);  // 書き込み位置を末尾へ移動する

	oss << 123 << std::flush;

	std::cout << oss.str() << std::endl;
}

実行結果:

test123

あるいは、ファイルストリームの場合と同様で、オープンモードを指定することが可能なので、 追記用のフラグを与えても良いです。

#include <iostream>
#include <sstream>

int main()
{
	std::string s("test");
	std::ostringstream oss(s, std::ios_base::out | std::ios_base::app);  // std::ios_base::app があると末尾へ書き込む

	oss << 123 << std::flush;

	std::cout << oss.str() << std::endl;
}

実行結果:

test123

文字列ストリームからの読み込み (istringstream)

文字列ストリームからの読み込みは、istringstream のオブジェクトに文字列を渡した後、 >>演算子などを使って、変数へ値を受け取っていきます。

#include <iostream>
#include <sstream>

int main()
{
	std::string s("10.55");
	std::istringstream iss(s);

	int i;
	double f;
	iss >> i >> f;
	
	std::cout << i << " " << f << std::endl;
}

実行結果:

10 0.55

ostringstream の場合と同じく、文字列ストリームに文字列を渡すときには、コピーが行われているので、 後から元の文字列を書き換えても、影響はありません

読み込みの場合は、文字列のフォーマットと、読込先の変数の型とが適切に一致していなければエラーが発生することに注意しなければなりません。 例えば、先ほどのサンプルプログラムで、変数 f の型が int型であったら、「10.55」の小数点の部分を読み取れずにエラーになります。

#include <iostream>
#include <sstream>

int main()
{
	std::string s("10.55");
	std::istringstream iss(s);

	int i;
	int f;
	iss >> i >> f;
	
	if (iss) {
		std::cout << i << " " << f << std::endl;
	}
	else {
		std::cout << "error" << std::endl;
	}
}

実行結果:

error


練習問題

問題@ 任意の型の数値を文字列に変換する関数テンプレートを実装して下さい。


解答ページはこちら

参考リンク

更新履歴

'2017/2/7 新規作成。



前の章へ

次の章へ

C++編のトップページへ

Programming Place Plus のトップページへ