C++編で扱っている C++ は 2003年に登場した C++03
という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14
-> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++
の学習を始めるのなら、C++11
よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい
C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。
この章の概要です。
標準ライブラリのストリームクラスを使って、文字列に対する入出力を行えます。これは、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章で紹介した内容はそのまま当てはまります。
文字列ストリームを使って、文字列へ書き込みを行うには、まず ostingstream のオブジェクトを生成し、そこへ <<演算子などの方法でデータを流し込んでいきます。そして、最終的に、strメンバ関数を呼び出すと、完成した文字列を basic_string型で受け取れます。
つまり、文字列ストリームは、ストリーム自身がバッファを持っており、そこへ書き込みを行い、任意のタイミングで、バッファの内容を返すという使い方になっています。メモリ管理の責任が文字列ストリーム側にあるため、バッファオーバーフローを起こすことも、メモリ解放を忘れてしまうこともなく安全であると言えます。
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream oss;
<< "test" << 123 << std::endl;
oss
std::cout << oss.str() << std::endl;
}
実行結果:
test123
このサンプルプログラムの場合、文字列ストリームへの書き込み時と、その結果を標準出力ストリームへ書き込む時の2か所で、std::endlマニピュレータを使っているため、改行が2度行われてしまいます。改行無しでフラッシュだけを行う std::flushマニピュレータを使えば、余分な改行をなくせます。マニピュレータについては、第30章であらためて解説します。
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream oss;
<< "test" << 123 << std::flush;
oss
std::cout << oss.str() << std::endl;
}
実行結果:
test123
引数がない strメンバ関数は、その時点でのバッファの内容のコピーを basic_string型で返します。また、引数に basic_string型の const参照を取る strメンバ関数もあり、こちらはバッファの内容を直接的に書き換えます。これは、バッファの内容をクリアしたいときに利用できる方法です。
.str(""); // バッファをクリア oss
ちなみに、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);
.seekp(0, std::ios_base::end); // 書き込み位置を末尾へ移動する
oss
<< 123 << std::flush;
oss
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 があると末尾へ書き込む
<< 123 << std::flush;
oss
std::cout << oss.str() << std::endl;
}
実行結果:
test123
文字列ストリームからの読み込みは、istringstream のオブジェクトに文字列を渡した後、>>演算子などを使って、変数へ値を受け取っていきます。
#include <iostream>
#include <sstream>
int main()
{
std::string s("10.55");
std::istringstream iss(s);
int i;
double f;
>> i >> f;
iss
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;
>> i >> f;
iss
if (iss) {
std::cout << i << " " << f << std::endl;
}
else {
std::cout << "error" << std::endl;
}
}
実行結果:
error
問題① 任意の型の数値を文字列に変換する関数テンプレートを実装してください。
新規作成。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |