C++編【標準ライブラリ】 第30章 入出力の書式化とマニピュレータ

先頭へ戻る

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

この章の概要

この章の概要です。

マニピュレータ

ストリームに対して、何らかの操作を行うオブジェクトをマニピュレータと呼びます。 以下は、標準で用意されている基本的なマニピュレータです。

マニピュレータ クラス 意味
flush basic_ostream バッファをフラッシュする
endl basic_ostream 改行文字('\n')を出力し、バッファをフラッシュする
ends basic_ostream 文字列の終端文字('\0')を出力する
ws basic_istream 書式化されない入力において、空白文字を読み飛ばす

マニピュレータは、<<演算子や >>演算子を使った入出力の際に用いられます。

std::cout << "xyz" << std::flush;

マニピュレータの正体は、引数と戻り値が basic_ostream や basic_istream の参照になっている関数です。 例えば、std::endl は次のように定義されています。

namespace std {
	template <typename T, typename Traits>
	basic_ostream<T, Traits>& endl(basic_ostream<T, Traits>& os)
	{
		os.put('\n');
		os.flush();
		return os;
	}
}

また、<<演算子や >>演算子は、関数ポインタを渡されると、 その関数ポインタを経由して関数を呼び出すようにオーバーロードされています

namespace std {
	basic_ostream& operator<<(basic_ostream<T, Traits>& (*func)(basic_ostream<T, Traits>&));
}

そのため、「std::cout << std::endl」のように書くと、関数ポインタを引数に取る operator<< が使用され、 間接的に std::endl関数の呼び出しが行われます。 std::endl関数は、改行文字の出力と、バッファのフラッシュを行った後、引数で渡されてきたストリームの参照をそのまま return します。
このような仕組みになっているため、ストリームオブジェクトに対する <<演算子や >>演算子は、連鎖的に記述していくことができます。 また、std::endl関数のような形の関数を自作すれば、自作のマニピュレータを作ることも可能です。


wsマニピュレータは、 例えば、std::getline関数(第27章)を使った入力のときに活用できます。

#include <iostream>
#include <sstream>

int main()
{
	std::istringstream iss("  xyz");

	std::string s;

	getline(iss, s);
	std::cout << s << std::endl;

	iss.seekg(0, std::ios_base::beg);  // 読み取り位置を先頭に戻す
	getline(iss >> std::ws, s);
	std::cout << s << std::endl;
}

実行結果:

  xyz
xyz

先ほど説明したように、マニピュレータは、ストリームオブジェクトの参照を受け取り、同じものを返しているので、 このサンプルプログラムのような使い方も可能です。
std::getline関数は、空白文字などは気にせず、1行分の文字列を読み取りますが、 wsマニピュレータの処理を経由させることで、空白文字の取り扱い方が変更されたストリームを渡すことになるため、 先頭部分の空白文字が読み飛ばされます。

書式フラグ

std::ios_baseクラスには、様々な書式に関する設定情報を管理する書式フラグや、 書式フラグを操作するためのメンバ関数が定義されています。 また、マニピュレータを使って操作を行うこともできます。

書式フラグには、以下のものがあります。

フラグ 意味
boolalpha bool型の値の表現に文字列形式を使う。
left 出力時、符号や値を左揃えにする。
right 出力時、符号や値を右揃えにする。
internal 出力時、符号を左揃え、値を右揃えにする。
showpos 出力時、正の数値に対して、+ の記号を付ける。
uppercase 出力時、数値表現に含まれるアルファベットを大文字にする(16進数や、科学的記数法(C言語編 第20章)に影響)
oct 数値を 8進数で入出力する。
dec 数値を 10進数で入出力する。
hex 数値を 16進数で入出力する。
showbase 数値の基数に応じた表現を付加する。
(8進数なら先頭に '0' を付加。16進数なら先頭に "0x" または "0X" を付加。uppercase の影響も受ける)
fixed 浮動小数点数に、10進数表記を用いる。
scientific 浮動小数点数に、科学的記数法(C言語編 第20章)を用いる。
showpoint 常に、小数点と、有効桁を埋めるための 0 を書き込む。
skipws 入力時に、先行する空白文字を読み飛ばす。
unitbuf 出力に関する演算を呼び出すたびに、バッファをフラッシュする。

これらの書式フラグは ios_baseクラスで定義されていますが、使用時には記述を短くするため、「ios::boolalpha」のように表記することがあります。 「ios」は、ios_baseクラスから派生した basic_iosクラステンプレートに対する typedef名で、以下のように定義されています。

namespace std {
	typedef basic_ios<char>    ios;
	typedef basic_ios<wchar_t> wios;
}

書式フラグを直接的に設定したければ、ios_baseクラスにある各種メンバ関数を用います。 fmtflags型は、先ほど挙げた書式フラグの組み合わせを扱うための型です。

fmtflags setf(fmtflags flags);                  // flags を ON にする
fmtflags setf(fmtflags flags, fmtflags mask);   // maskグループの書式フラグに flags を設定
void unsetf(fmtflags flags);                    // flags を OFF にする
fmtflags flags() const;                         // 現在のフラグを返す
fmtflags flags(fmtflags flags);                 // flags で上書きする

2つ目の setfメンバ関数について、mask という引数は、書式フラグのグループを指定します。 グループというのは、ある同じ設定項目に対するフラグが複数種類存在する場合に、それらをまとめて表現するものです。 例えば、数値の基数を指定する oct、dec、hex は1つのグループを構成しています。 実際には、以下のようにグループごとのマスクビットが用意されているので、このいずれかを指定します。

マスク 意味
adjustfield left、right、internal に対するマスク
basefield oct、dec、hex に対するマスク
floatfield fixed、scientific に対するマスク

C++11(書式フラグの追加)

C++11 では、floatfield のグループに、hexfloatdefaultfloat の2つの書式フラグが追加されています。

hexfloat は、浮動小数点数を 16進数で表記するように指示します。

defaultfloat は、浮動小数点数をデフォルトの方法で表現することを指示します。 この場合、出力する値や精度に応じて、通常の小数点表記や、科学的記数法による表現に切り替わります。 これは、printf関数(→リファレンス)の "%g"フォーマットに相当するものです。
このデフォルトの挙動は、C++03以前でも同様のものなので、単に明確な名前が用意されただけです。


以下にサンプルプログラムを挙げておきます。

#include <iostream>

int main()
{
	// 数値の符号を出力させる
	std::cout.setf(std::ios::showpos);
	std::cout << 10 << std::endl;

	// 基数を表現するテキストを付加し、アルファベットの部分は大文字で出力
	// 16進数で出力する
	// 符号の出力についての設定はそのまま
	std::cout.setf(std::ios::showbase | std::ios::uppercase);
	std::cout.setf(std::ios::hex, std::ios::basefield);
	std::cout << 10 << std::endl;

	// 現在の書式フラグを取っておく
	const std::ios::fmtflags flags = std::cout.flags();

	// アルファベットの大文字化について解除
	std::cout.unsetf(std::ios::uppercase);
	std::cout << 10 << std::endl;

	// 数値の符号を出力させる
	// 上書きになるので、その他のフラグは OFF に戻る
	std::cout.flags(std::ios::showpos);
	std::cout << 10 << std::endl;

	// 最初に取っておいた書式フラグで上書き
	std::cout.flags(flags);
	std::cout << 10 << std::endl;
}

実行結果:

+10
0XA
0xa
+10
0XA


また、パラメータを伴う必要性から、フラグの形を採っていない設定項目があり、 これらについては、以下の専用のメンバ関数を使って制御します。

// 以下は、ios_baseクラスで定義
streamsize width() const;                // フィールドの幅を返す
streamsize width(streamsize val);        // フィールドの幅を設定して、以前の幅を返す

streamsize precision() const;            // 浮動小数点数の精度を返す
streamsize precision(streamsize val);    // 浮動小数点数の精度を設定して、以前の精度を返す

// 以下は、basic_iosクラステンプレートで定義
T fill() const;    // 充填文字を返す
T fill(T c);       // 充填文字を設定して、以前の充填文字を返す

フィールドの幅は、出力時には最小の幅(文字数)を、入力時には最大の幅を意味します。

出力する文字数がフィールドの幅に満たないとき、足りない文字を充填文字で補います。 フィールドの幅のデフォルトは 0 で、これは特に何もしないことを表す値でもあります。 また、充填文字のデフォルトは空白です。
また、adjustfieldグループの書式フラグは、フィールドの幅に応じて、左寄せや右寄せといった処理を行います。

widthメンバ関数による設定については、次回の出力処理にだけ影響を与えることに注意して下さい。 この挙動は、次のサンプルプログラムで確認できます。

#include <iostream>

int main()
{
	std::cout.width(5);
	std::cout.fill('?');

	std::cout << "abc" << std::endl;
	std::cout << "abc" << std::endl;

	std::cout.width(5);
	std::cout.setf(std::ios::left, std::ios::adjustfield);
	std::cout << "abc" << std::endl;
}

実行結果:

??abc
abc
abc??

入力の場合の挙動も確認しておきましょう。

#include <iostream>

int main()
{
	char s[5];

	std::cin.width(sizeof(s));
	std::cin >> s;

	std::cout << s << std::endl;
}

実行結果:

abcde   <-- 入力した文字列
abcd

文字列の終端文字についても考慮されるため、5文字目は受け取られていないことが分かります。

widthメンバ関数の挙動は分かりづらいので、 毎回の出力時に、<<演算子の連鎖の中に、setwマニピュレータを、組み込んだ方が良いかも知れません。 その場合、例えば「std::cout << std::setw(5) << "abc" << std::endl;」のようになります。

マニピュレータによる書式フラグの制御

書式フラグを制御するマニピュレータとして、以下のものがあります。 これらは、ios という標準ヘッダに定義されています。

マニピュレータ 意味
boolalpha boolalphaフラグを設定する。
noboolalpha boolalphaフラグをクリアする。
left 符号や値を左揃えにする。
right 符号や値を右揃えにする。
internal 符号を左揃え、値を右揃えにする。
showpos showposフラグを設定する。
noshowpos showposフラグをクリアする。
uppercase uppercaseフラグを設定する。
nouppercase uppercaseフラグをクリアする。
oct 数値を 8進数で入出力する。
dec 数値を 10進数で入出力する。
hex 数値を 16進数で入出力する。
showbase showbaseフラグを設定する。
noshowbase showbaseフラグをクリアする。
showpoint showpointフラグを設定する。
noshowpoint showpointフラグをクリアする。
fixed 浮動小数点数に、10進数表記を用いる。
scientific 浮動小数点数に、科学的記数法を用いる。
skipws skipwsフラグを設定する。
noskipws skipwsフラグをクリアする。
unitbuf unitbufフラグを設定する。
nounitbuf unitbufフラグをクリアする。

また、書式に関するパラメータを制御するマニピュレータとして、以下のものがあります。 これらは、iomanip という標準ヘッダに定義されています。

マニピュレータ 意味
setiosflags(flags) 書式フラグに flags を設定する。
ストリーム s に対して、s.setf(flag) とすることと同じ。
resetiosflags(mask) 指定グループの書式フラグをクリアする。
ストリーム s に対して、s.setf(0, mask) とすることと同じ。
setw(val) フィールドの幅を設定する。
以前の値を返さないことを除いて、ストリーム s に対して、s.width(val) とすることと同じ。
setfill(c) 充填文字を設定する。
以前の値を返さないことを除いて、ストリーム s に対して、s.fill(c) とすることと同じ。
setbase(base) 基数を設定する。
base が 8、10、16 のいずれかならば、その基数が設定され、それ以外を指定するとデフォルトの設定に戻される。 デフォルトは、出力については 10進数、入力については数値の先頭に 0 や 0x といった基数を表す表記があればそれに従い、無ければ 10進数になる。
setprecision(val) 浮動小数点数の精度を設定する。
以前の値を返さないことを除いて、ストリーム s に対して、s.precision(val) とすることと同じ。


練習問題

問題@ フィールドの幅が 10 で、常に右寄せに出力する myformatマニピュレータを自作して下さい。

問題A myformatマニピュレータを改造して、以下のようにフィールドの幅を指定できるようにして下さい。

std::cout << myformat(10) << "xyz" << std::endl;

問題B myformatマニピュレータは、フィールドの幅をテンプレート引数で指定する形にすると、 動的な変更はできなくなりますが、より単純に実装できます。 以下のように使用できる myformatマニピュレータを作成して下さい。

std::cout << myformat<10> << "xyz" << std::endl;


解答ページはこちら

参考リンク

更新履歴

'2017/2/10 新規作成。



前の章へ

次の章へ

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

Programming Place Plus のトップページへ