サーチ…
備考
std::istream_iterator
デフォルトコンストラクタは、ストリームの終わりを表すイテレータを構築します。したがって、 std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), ....
は、 ifs
の現在の位置から最後にコピーすることを意味します。
ストリングストリーム
std::ostringstream
は、オブジェクトが出力ストリームのように見えるクラスです(つまり、 operator<<
を使用してオブジェクトに書き込むことはできますが)実際に書き込み結果を格納し、ストリームの形で提供します。
次の短いコードを考えてみましょう。
#include <sstream>
#include <string>
using namespace std;
int main()
{
ostringstream ss;
ss << "the answer to everything is " << 42;
const string result = ss.str();
}
この線
ostringstream ss;
そのようなオブジェクトを作成します。このオブジェクトは、最初に通常のストリームのように操作されます。
ss << "the answer to everything is " << 42;
それにもかかわらず、結果のストリームは次のように取得できます。
const string result = ss.str();
(文字列のresult
は"the answer to everything is 42"
と等しくなります)。
これは主に、ストリームのシリアル化が定義されているクラスがあり、文字列フォームが必要な場合に便利です。たとえば、あるクラスがあるとします
class foo
{
// All sort of stuff here.
};
ostream &operator<<(ostream &os, const foo &f);
foo
オブジェクトの文字列表現を取得するには、
foo f;
我々は使用することができます
ostringstream ss;
ss << f;
const string result = ss.str();
result
には、 foo
オブジェクトの文字列表現が含まれます。
最後までファイルを読む
行単位でテキストファイルを読む
ifstream
ドキュメントでは、通常、テキストファイルを行末まで読み込む適切な方法は不明です。初心者のC ++プログラマによる一般的な間違いや、ファイルを読む適切な方法を考えてみましょう。
空白文字のない行
わかりやすくするために、ファイル内の各行に空白記号が含まれていないと仮定します。
ifstream
はoperator bool()
があり、ストリームにエラーがなく読み取り可能な状態になったときにtrueを返します。さらに、 ifstream::operator >>
がストリーム自体への参照を返すので、非常に洗練された構文でEOF(エラーだけでなく)を読み込んでチェックすることができます:
std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
std::cout << s << std::endl;
}
空白文字を含む行
ifstream::operator >>
は、空白文字が現れるまでストリームを読み込みます。したがって、上記のコードでは、行の単語を別々の行に出力します。行末までをすべて読み込むには、 ifstream::operator >>
代わりにstd::getline
使用します。 getline
は、使用しているスレッドへの参照を返します。したがって、同じ構文を使用できます。
while(std::getline(ifs, s)) {
std::cout << s << std::endl;
}
明らかに、 std::getline
は、最後まで単一行のファイルを読み込むためにも使用されるべきです。
一度にファイルをバッファに読み込む
最後に、空白や改行などの文字を止めることなく、最初から最後までファイルを読み込みましょう。正確なファイルサイズまたは長さの上限が許容されることがわかっている場合は、文字列のサイズを変更してから読み取ることができます。
s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
s.begin());
それ以外の場合は、各文字を文字列の最後に挿入する必要があります。したがって、 std::back_inserter
が必要です。
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::back_inserter(s));
あるいは、イテレータの範囲引数を持つコンストラクタを使用して、ストリームデータを持つコレクションを初期化することもできます。
std::vector v(std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>());
これらの例は、 ifs
がバイナリファイルとして開かれている場合にも適用できます。
std::ifstream ifs("1.txt", std::ios::binary);
ストリームのコピー
ファイルはストリームとイテレータで別のファイルにコピーすることができます:
std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(ofs));
ofs.close();
または互換性のあるインタフェースを持つ他のタイプのストリームにリダイレクトされます。たとえばBoost.Asioネットワークストリーム:
boost::asio::ip::tcp::iostream stream;
stream.connect("example.com", "http");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(stream));
stream.close();
配列
イテレータはポインタの一般化として考えることができるので、上記の例のSTLコンテナはネイティブ配列に置き換えることができます。数字を配列に解析する方法は次のとおりです。
int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);
配列が割り当てられた後にオンザフライでサイズ変更することはできないため、バッファオーバーフローに注意してください。たとえば、上記のコードに100以上の整数が含まれるファイルが供給された場合、配列外に書き出して未定義の動作に移行しようとします。
iostreamでコレクションを印刷する
基本的な印刷
std::ostream_iterator
使用すると、STLコンテナの内容を明示的なループなしで任意の出力ストリームに出力できます。 std::ostream_iterator
コンストラクタの2番目の引数は区切り文字を設定します。たとえば、次のコード:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ! "));
印刷する
1 ! 2 ! 3 ! 4 !
暗黙の型キャスト
std::ostream_iterator
は、コンテナのコンテンツタイプを暗黙的にキャストできます。たとえば、小数点の後に3桁の浮動小数点値を出力するようにstd::cout
を調整しましょう。
std::cout << std::setprecision(3);
std::fixed(std::cout);
格納された値はint
ままですが、 float
でstd::ostream_iterator
をインスタンス化します。
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<float>(std::cout, " ! "));
上記のコードは結果を得ます
1.000 ! 2.000 ! 3.000 ! 4.000 !
std::vector
にもかかわらずint
が保持されます。
生成と変換
std::generate
、 std::generate_n
およびstd::transform
関数は、オンザフライでのデータ操作のための非常に強力なツールを提供します。例えば、ベクトルを持つ:
std::vector<int> v = {1,2,3,4,8,16};
各要素の "x is even"文のブール値を簡単に出力することができます:
std::boolalpha(std::cout); // print booleans alphabetically
std::transform(v.begin(), v.end(), std::ostream_iterator<bool>(std::cout, " "),
[](int val) {
return (val % 2) == 0;
});
または二乗された要素を印刷する:
std::transform(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
return val * val;
});
スペースで区切られたN個の乱数を印刷する:
const int N = 10;
std::generate_n(std::ostream_iterator<int>(std::cout, " "), N, std::rand);
配列
テキストファイルを読むセクションのように、これらの考慮事項のほとんどすべてがネイティブ配列に適用される可能性があります。たとえば、ネイティブ配列から二乗された値を出力します。
int v[] = {1,2,3,4,8,16};
std::transform(v, std::end(v), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
return val * val;
});
ファイルの解析
STLコンテナにファイルを解析する
istream_iterator
は、コード内の明示的なループなしで、数字やその他の解析可能なデータのシーケンスをSTLコンテナに読み込むのに非常に便利です。
明示的なコンテナサイズの使用:
std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin());
またはイテレータを挿入して:
std::vector<int> v;
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
std::back_inserter(v));
入力ファイル内の数字は、任意の数の空白文字と改行で除算できることに注意してください。
異種テキストテーブルの解析
istream::operator>>
は空白記号までテキストを読み込むので、複雑なデータテーブルを解析するためにwhile
条件で使用できます。たとえば、2つの実数とそれに続く文字列(空白なし)が各行にあるファイルがあるとします。
1.12 3.14 foo
2.1 2.2 barr
次のように解析することができます:
std::string s;
double a, b;
while(ifs >> a >> b >> s) {
std::cout << a << " " << b << " " << s << std::endl;
}
変換
範囲操作関数は、 std::istream_iterator
範囲で使用できます。それらのうちの1つはstd::transform
、データをオンザフライで処理することができます。たとえば、整数値を読み込み、それらに3.14を掛けて、結果を浮動小数点コンテナに格納します。
std::vector<double> v(100);
std::transform(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin(),
[](int val) {
return val * 3.14;
});