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++編を作成中です。
この章の概要です。
ストリームに対して、何らかの操作をおこなうオブジェクトをマニピュレータと呼びます。以下は、標準で用意されている基本的なマニピュレータです。
マニピュレータ | クラス | 意味 |
---|---|---|
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>
<T, Traits>& endl(basic_ostream<T, Traits>& os)
basic_ostream{
.put('\n');
os.flush();
osreturn os;
}
}
また、<<演算子や >>演算子は、関数ポインタを渡されると、その関数ポインタを経由して関数を呼び出すようにオーバーロード📘されています。
namespace std {
& operator<<(basic_ostream<T, Traits>& (*func)(basic_ostream<T, Traits>&));
basic_ostream}
そのため、「std::cout << std::endl」のように書くと、関数ポインタを引数に取る operator<< が使用され、間接的に std::endl関数の呼び出しが行われます。std::endl関数は、改行文字の出力と、バッファのフラッシュを行った後、引数で渡されてきたストリームの参照をそのまま return します。
このような仕組みになっているため、ストリームオブジェクトに対する <<演算子や >>演算子は、連鎖的に記述できます。また、std::endl関数のような形の関数を自作すれば、自作のマニピュレータを作ることも可能です。
wsマニピュレータは、たとえば、std::getline関数(第2章)を使った入力のときに活用できます。
#include <iostream>
#include <sstream>
int main()
{
std::istringstream iss(" xyz");
std::string s;
(iss, s);
getlinestd::cout << s << std::endl;
.seekg(0, std::ios_base::beg); // 読み取り位置を先頭に戻す
iss(iss >> std::ws, s);
getlinestd::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 flags); // flags を ON にする
fmtflags setf(fmtflags flags, fmtflags mask); // maskグループの書式フラグに flags を設定
fmtflags setfvoid unsetf(fmtflags flags); // flags を OFF にする
() const; // 現在のフラグを返す
fmtflags flags(fmtflags flags); // flags で上書きする fmtflags flags
2つ目の setfメンバ関数について、mask という引数は、書式フラグのグループを指定します。グループというのは、ある同じ設定項目に対するフラグが複数種類ある場合に、それらをまとめて表現するものです。たとえば、数値の基数を指定する oct、dec、hex は1つのグループを構成しています。実際には、以下のようにグループごとのマスクビットが用意されているので、このいずれかを指定します。
マスク | 意味 |
---|---|
adjustfield | left、right、internal に対するマスク |
basefield | oct、dec、hex に対するマスク |
floatfield | fixed、scientific に対するマスク |
以下にサンプルプログラムを挙げておきます。
#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クラスで定義
() const; // フィールドの幅を返す
streamsize width(streamsize val); // フィールドの幅を設定して、以前の幅を返す
streamsize width
() const; // 浮動小数点数の精度を返す
streamsize precision(streamsize val); // 浮動小数点数の精度を設定して、以前の精度を返す
streamsize precision
// 以下は、basic_iosクラステンプレートで定義
() const; // 充填文字を返す
T fill(T c); // 充填文字を設定して、以前の充填文字を返す T fill
フィールドの幅は、出力時には最小の幅(文字数)を、入力時には最大の幅を意味します。
出力する文字数がフィールドの幅に満たないとき、足りない文字を充填文字で補います。フィールドの幅のデフォルトは 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) | 基数を設定する。 10進数、入力については数値の先頭に 0 や 0x といった基数を表す表記があればそれ 10進数になる。 |
setprecision(val) | 浮動小数点数の精度を設定する。 |
問題① フィールドの幅が 10 で、つねに右寄せに出力する myformatマニピュレータを自作してください。
問題② myformatマニピュレータを改造して、以下のようにフィールドの幅を指定できるようにしてください。
std::cout << myformat(10) << "xyz" << std::endl;
問題③ myformatマニピュレータは、フィールドの幅をテンプレート実引数で指定する形にすると、動的な変更はできなくなりますが、より単純に実装できます。以下のように使用できる myformatマニピュレータを作成してください。
std::cout << myformat<10> << "xyz" << std::endl;
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |