std::istream の関数を使って値を読み込む - C++ プログラミング

PROGRAM


std::istream の関数を使って値を読み込む

C++ では std::istream という、バッファデータからのデータ取り込みを支援するクラスが用意されています。

これを使って値を入力する方法としては、ストリーム抽出演算子 (>>) を使った方法が有名ですけど、今回は read 関数などの関数を使って値を取得する方法について見て行きます。

 

ストリームから 1 文字読み込む

ストリームから 1 文字取得する場合には、get 関数や peek 関数が使えます。

// get 関数は、値を取得しつつ、読み込み位置を次へ進めます。

char a = stream.get();

 

// peek 関数は、値を取得しつつ、読み込み位置を現在のまま維持します。

char b = stream.peek();

どちらも、現在指している読み取り位置から 1 文字だけ読み取ることができます。

このとき、get 関数は読み取り位置をひとつ先に進めますが、peek 関数は現在の読み取り位置のまま移動しません。

 

このようにして 1 文字を読み取るとき、ストリームが末端まで移動している場合には、そのストリームの traits_type::eof() が返す値と同じ値が得られる様子でした。

たとえば istringstream を使用していて、get 関数で得られた値が末端を表すかどうかを知りたい場合は、次のように判定できます。

// 末端のために文字を取得できなかった場合は Traits::eof() と同じ値が得られます。

int word = stream.get();

 

// 末端でなければ(有効な文字が得られたら)処理をしたい場合は、Traits::eof() と一致しないかを確かめます。

if (word != std::istringstream::traits_type::eof())

{

}

ここで注意したいのが、末端の時に get 関数を呼んで初めて istringstream の EOF フラグが true にセットされるようなところです。

そのため、while (!stream.eof()) のようにしてループを回していても、最後に 1 回だけ、ストリームが末端を指した状態で get 関数が呼ばれてしまうようなので、末端の文字を無視したい場合は注意が必要してコーディングする必要がありそうです。

 

ちなみに、読み取り位置を 1 文字前に戻したいときには unget 関数を使います。

// unget 関数は、読み込み位置をひとつ前に戻します。

stream.unget();

試してみた感じでは、直前で get 関数で取得したかどうかにかかわらず、読み取り位置を素直に 1 文字前に移動するようでした。

 

ストリームから任意の長さの文字を読み込む

指定した長さの文字を読み込む

ストリームから任意の長さの文字を読み込みたいときには read 関数や readsome 関数が使えます。

 

どちらも引数に、読み込んだデータを格納するバッファと読み込む長さを指定しますが、戻り値がそれぞれ異なります。

read 関数の方は、自分自身の参照を戻り値に返します。readsome 関数の方は、戻り値で、読み取れた文字数を返します。

char* buffer1[100];

char* buffer2[100];

 

// read 関数では、自分自身を戻り値に返します。読み込んだ長さは gcount 関数で取得できます。

stream.read(buffer1, 15);

 

// readsome 関数は、読み込んだ長さを戻り値で返します。

size_t readed = stream.readsome(buffer1, 15);

ストリームが、指定した文字よりも多くの文字を持っている場合は、指定したバッファに文字データを書き込みます。

たとえば、バッファーサイズを 15 に指定して呼び出した場合は、15 文字のデータがバッファに取り込まれます。このとき、文字列の終わりを表す NUL 文字 ('\0') が末尾に設定されることはありません。

 

指定したバッファサイズよりもストリームが持つ文字が少ない場合は、ストリームが持っている分だけバッファを埋めます。このときも NUL 文字が末尾に設定されることはありません。

 

読み込んだ文字の長さを取得する

read 関数で、実際に何文字のデータが読み込めたかを知るためには gcount 関数を使います。

// 直前の操作で読み取れた文字数を取得できます。

size_t readed = stream.gcount();

 

// read 関数は自分自身の参照を返すので、そこから続けて gcount 関数を呼び出すのも有りです。

size_t readed = stream.read(buffer1, 100).gcount();

このようにすることで、直前の操作で読み取れた文字数を取得することができます。

 

改行までの文字を読み込む

改行が現れるまでの文字を読み込みたい場合には getline 関数を使います。

ここでいう改行というのは '\n' で表される文字で、Windows やインターネットで一般的な '\r\n' の場合は、最初の '\r' は文字として扱われるところに注意が必要です。

ただし std::ifstream など、ストリームによっては自動で '\n' に統一してくれたりするので、その辺りにも気を配る必要があります。

 

ちなみに第三引数に 1 文字を指定すれば、指定した文字を改行文字として扱います。

char* buffer3[100];

 

// getline 関数では、自分自身を戻り値に返します。読み込んだ長さは gcount 関数で取得できます。

stream.getline(buffer3, 100);

このようにすることで、改行文字が現れるところまでに現れた文字データをバッファに取り出すことができます。このとき、末尾には '\0' が挿入されます。

次の読み込み位置は、改行文字の次の文字になります。

 

ストリームが扱う 1 行が指定したバッファーよりも長い場合は、バッファに読み込めるまで読んでいったん終了します。

このときにも末尾に '\0' が挿入されるため、読みだされる文字データは、指定したバッファサイズよりも 1 つ小さい数になります。

 

gcount 関数で取得できる文字数

このとき gcount 関数で取得できる読み込んだデータ数は改行文字や末端のところまでになります。

バッファーに読み込まれた文字列の長さは、1 行全てをバッファに格納できた場合には、取得できた文字列の末尾に '\0' が挿入されるので、改行を除く gcount よりもひとつ小さな値になります。ただし取得で使うバッファは、末尾の '\0' を格納する分、gcount と同じ数だけ必要です。

バッファーに 1 行を読み切れない場合は、取得できた文字数と gcount の値が一致します。ただし、このときも取得した文字列の末尾には '\0' が挿入されるので、実際に読み込まれるデータは、取得バッファの長さよりも 1 文字短い文字列になります。

 

getline 関数で 1 行を読み切れない場合

getline 関数で 1 行のデータがバッファに収まりきらないとき、ストリームの fail フラグがセットされます。

このようにすることで、あらかじめ clear 関数を使ってフラグをリセットしてから readline 関数を呼び出すと、読み出した後で fail 関数を使って true が得られた場合には、行を読み切らなかったことが判定できます。

// たとえばこのテキストストリームに 1 行のデータを読み込むとします。

std::ostringstream textStream;

 

char buffer[10];

 

do

{

// フラグをリセットしてから、getline 関数で文字データを読み込みます。

stream.clear();

stream.getline(buffer, 10);

 

// 読み込めたテキストをストリームに追加します。

textStream << buffer;

 

// これを getline が途中までしか読み込めなかった間、繰り返します。

}

while (stream.fail());

 

std::cout << textStream.str() << std::endl;

ちなみに、改行がなくて末端までデータを読み込んだ場合には fail フラグがセットされないので、これで最後まで読み込んでも正しく処理されます。

 

std::istream の読み込み位置を移動する

指定した文字数だけストリームを読み捨てる

指定した文字数だけ読み進める

ストリームを任意の数だけ、文字を読み捨てて進めたい場合は ignore 関数を使います。

// ストリームを 5 文字読み捨てます。

stream.ignore(5);

こうすることで、ストリームを 5 文字分読み捨てるか、それ以前にストリームの末端が来るまで読み進めます。

末端の代わりに指定した文字を指定して、指定文字数読み捨てるか、その文字が現れるところで止めることもできます。

// ストリームを 5 文字読み捨てます。または第二引数で指定した境界文字まで読み進めます。

stream.ignore(5, '\n');

このようにして読み進めたとき、次の読み込み位置は、指定した文字の次を位置を示しています。

また、実際に何文字読み進めたかどうかは gcount 関数を使って確認できます。

 

指定した文字が次に登場した次まで読み進める

指定した文字数だけ読み進める ignore 関数では、第二引数で文字を指定すると、指定した文字数またはその文字が現れた次の位置まで移動することができます。

これを利用して、ストリームの中から、指定した文字の次の場所まで移動することもできます。

// 指定できる最大の数を指定することで、事実上、指定した文字の次へ読み込み位置を進められます。

stream.ignore(std::numeric_limits<std::streamsize>::max(), ',');

たとえばこのようにすることで、次のコロンが登場する位置の、次の場所へ読み位置を進めることができます。

 

読み込み位置を自由に変更する

ストリームの読み込み位置を自由に移動したい場合は seekg 関数を使います。

なお、この seekg 関数は、ストリームが扱うストリームバッファが seekpos 関数や seekoff 関数を実装している必要があります。このあたりの詳細は 自由に移動できるストリームバッファを作成する で記しています。

 

ちなみに、書き込み用のストリームで、書き込み位置を移動する場合は seekp 関数を使います。

このとき、当たり前とも言えますけれど、ストリームの failbit フラグがセットされている場合は移動できないので注意が必要です。移動したい場合はいったん clear 関数を実行して failbit をリセットするようにします。

 

絶対位置を指定して移動したい場合は、seekg 関数に移動したい位置を 0 から始まるインデックス番号で指定します。

// ストリームの読み込み位置を先頭に移動します。

stream.seekg(0);

 

相対位置で移動したい場合には、移動位置と、どこからの相対位置で移動するかを指定します。

// ストリームの読み込み位置を、現在位置から前に 2 つ移動します。

stream.seekg(-2, std::ios_base::cur);

ここで指定できる相対位置には、次の 3 つがあります。

std::ios_base::beg 先頭からの相対移動です。
std::ios_base::end 末尾からの相対移動です。
std::ios_base::cur 現在位置からの相対移動です。

 


[ もどる ]