静岡理工科大学 菅沼ホーム 全体目次 演習解答例 付録 索引

第16章 入出力クラス

  1. 16.1 標準入出力
    1. (プログラム例 16.1 ) 標準入出力
    2. (プログラム例 16.2 ) クラスの出力
  2. 16.2 書式付き出力
    1. (プログラム例 16.3 ) 書式付き出力(メンバー関数)
    2. (プログラム例 16.4 ) 書式付き出力(書式操作子)
  3. 16.3 ファイル入出力
    1. (プログラム例 16.5 ) ファイル入出力
  4. 演習問題16

  ここでは,C++ における入出力クラスについて説明します.Java にも多くの入出力を扱うためのクラスが用意されていますが,科学技術計算に重点を置くといった目的から,Java に関しては,プログラム例に基づき,その一部だけについて説明します.詳細については,付録の入出力クラスを参照して下さい.

16.1 標準入出力

  C と同じように,C++ においても以下の 4 つのストリームがプログラム実行開始と共に開いています.
	cin  : 標準入力
	cout : 標準出力
	cerr : 標準エラー出力
	clog : バッファ付き出力
		
  C++ の入出力システムは,階層的に構成されたストリームに関するクラスとして定義され,iostream というヘッダファイルに含まれています.入出力の最も基本的なクラスは ios であり,そこから strstreambaseistream,及び,ostream の 3 つのクラスが派生しています.また,iostream は,istream 及び ostream から派生しています.

(プログラム例 16.1 ) 標準入出力 

  cin や cout を使用することにより,以下の例に示すように,出力する変数の型を意識しないで,入出力が可能になります.また,行内のスペースを無視したくない場合は,istream のメンバー関数 getline を使用します.最大 (MAX_LINE - 1) 文字を読み込み,最後に NULL 文字が付加されます.デフォルトの行区切り文字は改行文字ですが,それ以外の区切り文字を指定したいときは,getline の 3 番目の引数として指定可能です.なお,istream のメンバー関数 get を使用して 1 行ずつ,または,1 文字ずつの入力も可能です.詳細については,付録を参照してください.

/****************************/
/* 標準入出力               */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>

#define MAX_LINE 50

int main()
{
	char line[MAX_LINE];
	std::cout << "1行,適当な文字列を入力して下さい ";
	std::cin.getline(line, MAX_LINE);

	int i;
	std::cout << "整数を入力して下さい ";
	std::cin >> i;

	double x;
	std::cout << "実数を入力して下さい ";
	std::cin >> x;

	char c[10];
	std::cout << "文字列を入力して下さい ";
	std::cin >> c;

	std::cout << "整数 " << i << " 実数 " << x << " 文字列 " << c <<"\n";
	std::cout << line << "\n";

	return 0;
}
		

  このプログラムの出力は,例えば,以下のようになります.
	整数 10 実数 3.14 文字列 suzuki
	test input
		
(プログラム例 16.2 ) クラスの出力

  演算子 << をオーバーロードすることによって,クラスの内容を通常の変数と同じように出力可能になります.次のプログラムのクラス Xyz は,3 次元空間の点の座標を定義しています.なお,演算子のオーバーロードを定義している関数において,cout を使用しないのは,オーバーロードを任意のストリームに対応させるためです.

/****************************/
/* クラスの出力             */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>

/*******************/
/* クラスXyzの定義 */
/*******************/
class Xyz
{
	public:
		int x, y, z;
		Xyz(int a, int b, int c)   // コンストラクタ
		{
			x = a;
			y = b;
			z = c;
		}
		friend std::ostream& operator << (std::ostream &, Xyz);   // <<のオーバーロード
};

/********************************************/
/* 座標の表示(演算子<<のオーバーロード) */
/********************************************/
std::ostream & operator << (std::ostream &stream, Xyz ten)
{
	stream << "   (" << ten.x << ", ";
	stream << ten.y << ", ";
	stream << ten.z << ")\n";
	return stream;
}

/************/
/* main関数 */
/************/
int main()
{
	Xyz a(0, 10, 20), b(-10, 5, 50);

	std::cout << a << b;

	return 0;
}
		

  このプログラムの出力は,以下のようになります.
	(0, 10, 20)
	(-10, 5, 50)
		

16.2 書式付き出力 

  1. ios のメンバー関数による方法

      C++ においては,書式制御フラッグが,long 型の整数として保存されていますので,各フラッグを on にしたり,off にすることによって,書式制御が可能です.特定のフラッグを on にするには次の ios のメンバー関数を使用します.
    	long setf(long <フラッグ>);
    			
    ただ,ビットを直接操作するのは面倒ですので,各フラッグには以下のような名前が付いています.
    	skipws     = 0x0001 : ストリームの入力中,先頭の空白文字を読み飛ばす
    	left       = 0x0002 : 出力を左寄せ
    	right      = 0x0004 : 出力を右寄せ
    	internal   = 0x0008 : 符号と数字の間にブランクを入れ出力幅一杯にする
    	dec        = 0x0010 : 10進表記(デフォルト)
    	oct        = 0x0020 : 8進表記
    	hex        = 0x0040 : 16進表記
    	showbase   = 0x0080 : 数値の基数の表示
    	showpoint  = 0x0100 : 浮動小数点の出力で小数点,右端の0,eを表示
    	uppercase  = 0x0200 : 浮動小数点のe等を大文字で表示
    	showpos    = 0x0400 : 正の数値の前に+を表示
    	scientific = 0x0800 : 実数値を浮動小数点表示
    	fixed      = 0x1000 : 固定小数点表示(デフォルト)
    	unitbuf    = 0x2000 : 出力の度に,すべてのストリームをフラッシュ
    	stdio      = 0x4000 : 出力の度に,stdout及びstderrをフラッシュ
    			
    従って,例えば,showbase を on にしたければ,
    	stream.setf(ios::showbase);
    			
    と記述します.ここで,stream は作用させたいストリームの名前です.また,複数のフラッグを,例えば,
    	cout.setf(ios::scientific | ios::showpos);
    			
    のように論理和を利用して記述することにより,on にすることも可能です.

      そのほか,書式フラッグを扱う関数としては,以下のようなものがあります.
    	long unsetf(long <フラッグ>); // フラッグをoff
    	long flags();                 // フラッグの現在値を返す
    	long flags(long <フラッグ>);  // フラッグをonにし,直前の値を返す
    			
    また,書式制御フラッグだけでなく,次の関数を使用すれば,出力幅,小数点以下の桁数等の制御も可能です.なお,以下の関数はすべて,その直前の値を返します.
    	int width(int <長さ>);      // 出力幅制御
    	char fill(char <文字>);     // 空白を埋める文字
    	int precision(int <桁数>);  // 小数点以下の桁数(デフォルトは 6 桁.固定小数点の場合は,小数点や符号を除いた全体の桁数になり,表示できないときは浮動小数点表示となる)
    			
    (プログラム例 16.3 ) 書式付き出力(メンバー関数)

    /********************************/
    /* 書式付き出力(メンバー関数) */
    /*      coded by Y.Suganuma     */
    /********************************/
    #include <iostream>
    using namespace std;
    
    int main()
    {
    	cout.setf(ios::showpos);         // +の表示
    	cout.setf(ios::scientific);      // 浮動小数点表示
    	cout << 123.45 << " " << 3.141592654 << "\n";
    
    	cout.precision(4);               // 小数点以下4桁
    	cout.width(10);                  // 出力幅10桁
    	cout << 123.45 << " " << 3.141592654 << "\n";
    
    	cout.fill('*');                  // *で埋める
    	cout.width(15);                  // 出力幅15桁
    	cout << 123.45 << " " << 3.141592654 << "\n";
    
    	return 0;
    }
    			

      このプログラムの出力は以下のようになります.
    	+1.234500e+002 +3.141593e+000
    	+1.2345e+002 +3.1416e+000
    	***+1.2345e+002 +3.1416e+000
    			
  2. 書式操作子による方法

      あと一つの書式制御の方法は,書式操作子という特殊な関数を入出力文の中で指定する方法です.書式操作子には以下のようなものがあります.なお,書式操作子を使用するためには,ヘッダファイル iomanip をインクルードしておく必要があります.
    	dec                  : 10 進表記
    	endl                 : 改行文字の出力
    	ends                 : 空白文字の出力
    	flush                : ストリームのフラッシュ
    	hex                  : 16 進表記
    	oct                  : 8 進表記
    	resetioflags(long f) : f で指定されたフラッグを off
    	setbase(int base)    : 基数を base にする
    	setfill(int ch)      : 文字 h で埋める
    	setioflags(long f)   : f で指定されたフラッグを on
    	setprecision(int p)  : 小数点以下 p 桁(デフォルトは 6 桁.固定小数点の場合は,小数点や符号を除いた全体の桁数になり,表示できないときは浮動小数点表示となる)
    	setw(int w)          : 出力幅を w
    	ws                   : 先頭の空白を読み飛ばす
    			
    (プログラム例 16.4 ) 書式付き出力(書式操作子)

      プログラム例 16.3 と同じ内容を,書式操作子を使用して書いた例です.

    /******************************/
    /* 書式付き出力(書式操作子) */
    /*      coded by Y.Suganuma   */
    /******************************/
    #include <iostream>
    #include <iomanip>
    using namespace std;
    
    int main()
    {
    	cout << setiosflags(ios::showpos);
    	cout << setiosflags(ios::scientific);
    	cout << 123.45 << " " << 3.141592654 << endl;
    						  // 次のように,命令と出力値を一緒に書いても構わない
    	cout << setprecision(4) << setw(10) << 123.45 << " " << 3.141592654 << endl;
    
    	cout << setfill('*');
    	cout << setw(15);
    	cout << 123.45 << " " << 3.141592654 << endl;
    
    	return 0;
    }
    			

      このプログラムの出力は以下のようになります.
    	+1.234500e+002 +3.141593e+000
    	+1.2345e+002 +3.1416e+000
    	***+1.2345e+002 +3.1416e+000
    			

16.3 ファイル入出力

  ファイルを利用するとき,入力の場合はそのストリームを ifstream クラス,出力の場合は ofstream クラス,また,入力と出力を行う場合は fstream クラスであることを宣言しなければなりません.

  宣言したストリームと,対象とするファイルを関連づけるのが次のメンバー関数です.
	void open(char *<ファイル名>, int <モード>, int <アクセス>);
		
  モードは,ファイルをどのような状態で開くかを宣言するもので,以下の中から選択します(論理和 | で結合し,一つ以上指定可).
	ios::app       : 後ろに追加出力
	ios::ate       : ファイルが開かれたときファイルの終わりまでシーク
	ios::in        : 入力ファイル
	ios::nocreate  : ファイルが存在しないと実行不可能
	ios::noreplace : ファイルがすでに存在すると実行不可能
	ios::out       : 出力ファイル
	ios::trunc     : ファイルが存在するとそれを大きさ 0 のファイルにする
		
  アクセスは,どのようにファイルにアクセスできるかを決めるもので,以下の中から選択されます.
	0 : 通常ファイル
	1 : 読み出し専用ファイル
	2 : 隠しファイル
	4 : システムファイル
	8 : アーカイブビットが on のファイル
		
  例えば,通常のファイル test.txt を,出力用にオープンするには,
	ofstream st;
	st.open("test.txt", ios::out, 0);
		
と記述すれば良いことになります.なお,オープンに失敗すると,ストリーム変数の値は 0 になります.

  通常のファイルを入力や出力でオープンする場合は,アクセスやモードに対しデフォルト値を所有しているため,ファイル名だけの指定で十分です.また,ifstream 等のクラスの定義にはファイルを自動的に開くコンストラクタが記述されていますので,例えば,上の例は,
	ofstream st("test.txt");
		
と書くだけで十分です.ただし,入出力を同時に行うような場合は,次のように,モード指定も必要になります.
	fstream st("test.txt", ios::in | ios::out);
		
  また,以下のようなメンバー関数を使用することによって,ファイルの状態を検出できます.
	int eof()        : ストリームの終わりであれば非 0 値,そうでなければ 0 を返します
	int fail()       : ストリームの演算に失敗したとき非 0 値,そうでなければ 0 を返します
	int bad()        : fail と似ていますが,もっと悪い状態を表します
	int good()       : ストリームに問題がないとき非 0 値,そうでなければ 0 を返します
	int operator !() : fail と同じ意味の演算子です.例えば,以下のように使用します.
	                       if (!std::cout)
	                          失敗したときの処理
	operator void*() : 最後のストリーム演算に失敗したとき 0 値,そうでなければ非 0 値を返します.
	                   例えば,以下のように使用します.
	                       if (std::cin>>c)
	                           ・・・
	                       else
	                           失敗したときの処理
		

(プログラム例 16.5 ) ファイル入出力 

  次のプログラムは,プログラム例 16.2 と同じような内容を扱っています.ただし,各点の座標をファイル test1.txt から読み込み,その座標を元に Xyz クラスのインスタンス a を生成し,かつ,その結果をファイル test.txt に出力しています.演算子 << のオーバーロードがファイル出力に対しても有効であることに注意して下さい.

/****************************/
/* ファイル入出力           */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;

/*******************/
/* クラスXyzの定義 */
/*******************/
class Xyz
{
	public:
		int x, y, z;
		Xyz(int a, int b, int c)   /* コンストラクタ */
		{
			x = a;
			y = b;
			z = c;
		}
	friend ostream& operator << (ostream &, Xyz);
};

/******************************************/
/* 座標の表示(演算子<<のオーバーロード) */
/******************************************/
ostream & operator << (ostream &stream, Xyz ten)
{
	stream << "   (" << ten.x << ", ";
	stream << ten.y << ", ";
	stream << ten.z << ")\n";
	return stream;
}

/************/
/* main関数 */
/************/
int main()
{
	int n1, n2, n3;
				// ファイルのオープン
	ifstream in("test1.txt");
	if (!in) {
		cout << "***error  入力ファイルを開けません\n";
		exit(1);
	}

	ofstream out("test.txt");
	if (!out) {
		cout << "***error  出力ファイルを開けません\n";
		exit(1);
	}
				// データ入力と出力
	while (!in.eof()) {
		in >> n1 >> n2 >> n3;
		Xyz a(n1, n2, n3);
		out << a;
	}
				// ファイルのクローズ
	in.close();
	out.close();

	return 0;
}
		

  このプログラムを実行すると,ファイル test1.txt の内容が,
	10 20 5
	5 4 10
		
であった場合,次のような出力がファイル test.txt になされます.
	(10, 20, 5)
	(5, 4, 10)
		

演習問題16

[問1]特定の月のカレンダーを出力するプログラムを,cout を利用して書け.

[問2]任意の数のテキストファイルを連結して出力する,つまり,
cat file1 file2 ・・・
を実現するプログラムを書け.

静岡理工科大学 菅沼ホーム 全体目次 演習解答例 付録 索引