テキストファイル入出力
項目単位での入力
バイナリファイル入出力については別のウェブサイト<バイナリ・ファイル入出力>で説明している。
なお、C++の説明を<岡本安晴「大学生のための心理学VC++プログラミング入門」>で行っている。
標準C++にはファイル入出力用のクラス型(ストリーム)が用意されている。入力用がifstreamであり、出力用がofstreamである。これらを使用するときはヘッダーファイルfstreamを
#include <fstream>
と#includeしておく。これらのクラス型のオブジェクトを
ifstream fin;
ofstream fout;
と定義しておくと、それらをそれぞれ「ファイル名1」および「ファイル名2」のファイルに結合して使うときは関数open()を用いて
fin.open(ファイル名1);
あるいは
fout.open(ファイル名2);
を実行する。ファイル名を表わす文字列は「char *」型あるいはstring型で与える。「char *」型を用いるときは
char nm[100];
としておけば、nmに設定した名前のファイルは
fin.open(nm);
を実行することにより読み出し用ファイルとすることができる。
string型を用いるときは、例えば
string s;
として、sに設定した名前のファイルは
fin.open(s);
を実行することにより読み込み用ファイルとすることができる。ただし、fin.open(実引数)における実引数としてstring型を用いることが出来るのはVisual C++2010の場合で、Visual C++2008あるいは2005ではエラーとなる。
上のファイルとの結合に失敗すると関数fail()の値がtrueになる。したがって、open()の実行後、fail()を呼び出せば、ファイルのオープンに成功したかどうかがわかる。ifstreamの場合、入力ファイルとして指定したファイル名のファイルが存在しないとき、fail()はtrueを返す。
ifstream型あるいはofstream型のオブジェクトの使い方はcinあるいはcoutと同じである。
入出力が終われば、関数close()を実行する。
サンプルプログラムを以下に示す。
#include "stdafx.h"
#include <iostream>
#include <fstream> // ifstream, ofstream
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
ifstream fin;
ofstream fout;
char c[100];
cout << "Data file name = ";
cin >> c;
fin.open(c);
if (fin.fail()){
cout << "Opening the input file failed." << endl;
cout << "何か整数値を入力して終了。" << endl;
int ck;
cin >> ck;
return 9;
}
cout << "Output file name = ";
cin >> c;
fout.open(c);
int n;
fin >> n;
fout << "No. of data = " << n << endl;
double * x, * y;
x = new double[n];
y = new double[n];
for (int i = 0; i < n; i++)
fin >> x[i] >> y[i];
fout << "\nData.." << endl;
double sx = 0.0, sy = 0.0;
for (int i = 0; i < n; i++){
fout << "x[" << i << "] = " << x[i] << " y[" << i << "] = " << y[i] << endl;
sx += x[i];
sy += y[i];
}
fout << endl << "mean_x = " << sx/10.0 << " mean_y = " << sy/10.0 << endl;
fin.close();
fout.close();
cout << n << " 組みのデータが処理されました。" << endl;
cout << "何か整数値を入力すると終了します。" << endl;
cin >> n;
return 0;
}
上のコードは、図1のようなファイルからデータを読み込むものである。
図1
このデータファイル「data.txt」は、第1行目にデータ(2つの値で1組のデータ)の個数(組数)が書かれていて、第2行目から1行ごとに1組のデータが並べられているものである。第1行目にデータの個数を書かない形式のデータの場合はこのウェブページの後半で説明する。上のコードではこの組数を
int n;
fin >> n;
と変数nに読み込んでいる。その後、
x = new double[n];
y = new double[n];
for (int i = 0; i < n; i++)
fin >> x[i] >> y[i];
と、配列xとyを用意して1組ずつ読み込んでいる。
上のコードの実行時のコンソール画面を図2に示す。
図2
実行時のカレントディレクトリはプログラムソースコードファイル*.cppと同じフォルダであるので、図2のようにファイル名のみを指定する場合は、入力ファイルはプログラムソースコードファイルと同じフォルダに用意し、出力ファイルもその同じフォルダに出力される。
入力用データファイルは以下のように作成する。
メニュ「File|New|File」を選ぶ(図3)。
図3
New Fileダイアログボックスが表示される(図4)。
図4
Templateとして、GeneralをクリックしてからText Fileをクリックして選び、Openボタンをクリックする。図5のようなエディタウィンドウの状態になる。
図5
TextFile2.txtが新規作成されたテキストファイルである。ファイル名における数字の2は、何番目に新規作成されたものであるのかを示す通し番号で、自動的に付けられる。これに名前を付けてプログラムと同じフォルダにおく。まず、メニュ「File|Save ***.txt As...」を選ぶ(図6)。「***.txt」は当該のファイル名である。
図6
Save File Asダイアログボックスが表示される(図7)。
図7
ファイルの種類が「Text Files (*.txt)」となっているので、テキストファイルだけが表示されているが、プログラムソースファイルFileStream.cppもこのフォルダにある。これは、上段に「FileStream ► FileStream」とソリューション名とプロジェクト名が並んでいて、ファイル名と一緒にDebugフォルダが表示されていることからわかるが、表示ファイルの種類を「All Files (*.*)」と全てのファイルを指定しても確認できる。このとき、図8のようにFileStream.cppが存在していることが確認できる。
図8
図7あるいは図8の状態でSaveボタンをクリックするとエディタウィンドウが図9のようになる。
図9
指定した名前(上の場合、data2.txt)になっていることがわかる。データを打ち込んでから、プログラムを実行する前に保存ボタンをクリックして保存する(図10)。
図10
エディタウィンドウにデータを書き込んだだけでは、その内容はファイルに反映されない。プログラムを実行してファイルからデータを読み込む前に必ず保存ボタンをクリックして保存しておく。
入力データファイルが上のdata.txtであり、出力ファイル名がtemp.txtのとき、プログラムの実行終了後、出力ファイルtemp.txtを開くと図11のようになっている。
図11
上の例では、データは先頭行にデータの組数が記載されている。これを図12のようにデータの個数はファイルに示されていないが、データがスラッシュ/で始まる行で挟まれている場合は、スラッシュ/で挟まれているデータの行数を数えてデータの組数を求めることができる。
図12
このスラッシュ/で挟まれた部分のデータ数の計算は、例えば以下のように行う。まず、最初のスラッシュ/の位置までファイルを読み進める。
string s;
while(true){
fin >> s;
if (s[0] == '/') break; // 最初の/の行まで読み飛ばす
}
ifstream型の入力ストリームfinによる読み込みでは、データファイルの改行も空白などと同じく区切り文字として扱われるので、「fin >> s;」を繰り返してstring型の変数sにスラッシュ/で始まる文字列が読み込まれるまで読み進んでいる。s[0]はsに読み込まれた文字列の先頭文字を表す。最初のスラッシュ/が読み込まれたら、次のスラッシュ/までのデータ数を以下のように数える。
int n = 0;
while(true){
fin >> s;
if (s[0] == '/') break; // 次の/の行まで数える
n++;
}
n /= 2; // 1行に2個のデータ
図12のデータファイルでは、1行に2個のデータ値が書かれているので、読み込まれたデータ値の総数が設定されているnを2で割ることにより、データ行の数(データの組数)が求まる。データの行数が判れば、後は上のプログラム例と同じように計算することができる。プログラム例を以下に示す。
#include "stdafx.h"
#include <iostream>
#include <fstream> // ifstream, ofstream
#include <string> // string型使用のため
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
ifstream fin;
ofstream fout;
string s, s0, s1;
cout << "Data file name = ";
cin >> s0;
fin.open(s0);
if (fin.fail()){
cout << "Opening the input file failed." << endl;
cout << "何か整数値を入力して終了。" << endl;
int ck;
cin >> ck;
return 9;
}
cout << "Output file name = ";
cin >> s1;
fout.open(s1);
int n = 0;
while(true){
fin >> s;
if (s[0] == '/') break; // 最初の/の行まで読み飛ばす
}
while(true){
fin >> s;
if (s[0] == '/') break; // 次の/の行まで数える
n++;
}
n /= 2; // 1行に2個のデータ
fout << "No. of data = " << n << endl;
fin.close();
fin.open(s0); // 再度、入力データファイルを開きなおす。
while(true){
fin >> s;
if (s[0] == '/') break;
}
double * x, * y;
x = new double[n];
y = new double[n];
for (int i = 0; i < n; i++)
fin >> x[i] >> y[i]; // 1行から2個のデータを読み込む
fout << "\nData.." << endl;
double sx = 0.0, sy = 0.0;
for (int i = 0; i < n; i++){
fout << "x[" << i << "] = " << x[i] << " y[" << i << "] = " << y[i] << endl;
sx += x[i];
sy += y[i];
}
fout << endl << "mean_x = " << sx/10.0 << " mean_y = " << sy/10.0 << endl;
fin.close();
fout.close();
cout << n << " 組みのデータが処理されました。" << endl;
cout << "何か整数値を入力すると終了します。" << endl;
cin >> n;
return 0;
}
項目単位での読み込みではなく、行単位で読み込みを行うプログラム例は<テキストファイルの行単位読み込みと例外の生成>に用意した。
データをテキストファイルから読み込み、計算結果をテキストファイルに書き出す簡単な統計分析プログラム例は<簡単な統計分析プログラミング例>に用意した。
なお、Visual C++によるプログラミング入門書として<岡本安晴「大学生のための心理学VC++プログラミング入門」勁草書房>を上梓した。
LinuxでのC++プログラミングについては<C++プログラミング/Linux>で説明している。