第八章 流和文件
目录标题
8.1标准输入/输出流
流:用于程序输入或输出的字符序列。
换行符:把一个流分成一系列行的字符。
8.1.1每次读取一个字符
get函数用于每次读取一个字符。
put函数用于每次显示一个字符。
注意:直到get函数读取标注流结束的文件结束字符以后,eof函数才会返回真值。
例子:处理流中的各个字符
#include<iostream>
#include<string>
using namespace std;
#define ENDFILE "CTRL-Z"
int main()
{
const char NWLN='\n'; //换行符字符串
char next; //下一个字符串在当前行
int charCount; //字符串的数字在当前位置
int lineCount; //保持栈的数据行在文件中
lineCount =0;
cout<<"Enter a line or press"<<ENDFILE<<": ";
cin.get(next); //得到第一个字符在新的行
while(!cin.eof())
{
charCount=0; //初始化新的行
while(next!=NWLN)
{
cout.put(next);
charCount++; //增加字符数量
cin.get(next); //得到下一个字符串
} //结束里面的while
cout.put(NWLN); //标记当前显示字符串的结束
lineCount++;
cout<<"Number of characters in line "<<lineCount<<" is "<<charCount<<endl;
cout<<"Enter a line or press "<<ENDFILE<<": ";
cin.get(next); //得到新的字符串
}//结束外面的while
cout<<endl<<endl<<"Number of lines processed is "<<lineCount<<endl;
return 0;
}
8.1.2程序风格—使用流作为条件
c++程序员可选择使用eof函数来测试流是否结束,也可以通过流名直接作为条件以测试流的状态。
注意:输入操作(cin.get(next))只在循环重复条件中出现。
8.2外部文件
8.2.1交互式与批处理
批处理:通过外部文件而不是与用户交互读取数据。
回显:读到的数据值的马上显示。
硬拷贝:程序输出到纸上的永久拷贝。
8.2.2外部文件的目录名
若要访问程序中的文件,必须知道文件的目录名。目录名由文件的路径及其之后的文件名构成。
文件名必须遵循特定计算机系统所应用的协议。
8.2.3将流连接到文件
c++中,使用open语句连接一个流对象到一个外部文件。
为了安全,避免原始文件丢失,做好文件的备份或拷贝是一个好主意。
程序中,在读或写外部文件之前,应该首先为程序所要处理的每个流/文件声明一个流对象。
例子:拷贝文件
#include<cstdlib>
#include<fstream>
#include<iostream>
using namespace std;
//将流对象与外部文件名相关联
#define inFile "InData.txt" //inFile的目录名
#define outFile "OutData.txt" //outFile的目录名
//使用函数
//复制一行文本
int copyLine(ifstream&, ofstream&);
int main()
{
int lineCount; //输出:生产线数量
ifstream ins; //ins是一个输入的流
ofstream outs; //outs是一个输出的流
//打开输入和输出的文件,存在任意的错误
ins.open(inFile); //连接ins到文件inFile
if(ins.fail())
{
cerr <<"*** ERROR:Cannot open "<<inFile<<"for input."<<endl;
return EXIT_FAILURE; //错误返回
}
outs.open(outFile); //连接outs到文件outFile
if(outs.fail())
{
cerr<<"*** ERROR:Cannot open "<<outFile<<"for output."<<endl;
return EXIT_FAILURE; //错误返回
}
//复制每个元素从inData到outData.
lineCount=0;
while(!ins.eof())
{
if(copyLine(ins,outs)!=0)
lineCount++;
}
//在屏幕上显示信息
cout<< "Input file copied to output file."<< endl;
cout<<lineCount<<"lines copied."<<endl;
ins.close(); //关闭输入文件流
outs.close(); //关闭输出文件流
return 0; //成功返回
}
//复制文件的一行到另一个文件
//pre: 输入打开以进行输入,输出打开输出
//post: ins的下一行被写到输出。最后的元素过程从ins是<nwln>;
// 最后的元素被写到outs是<nwln>
//返回:元素复制的数量
int copyLine(ifstream& ins,ofstream& outs)
{
const char NWLN='\n'; //新行的元素
char nextCh; //输入:元素缓冲区
int charCount=0; //元素复制的数量
//复制所有的元素从输入流到输出流
ins.get(nextCh);
while((nextCh!=NWLN)&&!ins.eof())
{
outs.put(nextCh);
charCount++;
ins.get(nextCh);
}//结束while
//如果最后的元素读的是NWLN写它到输出
if(!ins.eof())
{
outs.put(NWLN);
charCount++;
}
return charCount;
}//结束复制流
cerr:标准错误流,通常连接到显示器。
8.2.4copyLine函数
所有文件流参数都必须进行引用传递。
8.2.5换行符的更多信息
8.2.6使用getline处理文件流
8.2.7程序风格—读取文件名
8.3使用外部文件进行程序间的通信
例子:准备一个工资文件
问题:写二个用于处理公司工资的程序。第一个程序读取由员工工资数据组成的数据文件,每位员工的数据以单独的数据记录存储,数据记录包含员工的名,姓,工时以及时薪,由三条数据记录组成的文件如下所示:
Jim Baxter 35.5 7.25
Adrian Cybriwsky 40.0 6.50
Ayisha Mertens 20.0 8.00
第一个程序为每位员工读取输入数据并且计算该员工的工作额,工作额是工时与时薪的乘积。然后,第一个程序将员工的名字和工资额写到输出文件,并且计算全公司薪水册的总工资数。处理完所有员工记录后,显示整个薪水册。处理上面的输入文件样本会产生如下的输出文件样本:
Jim Baxter $257.38
Adrian Cybriwsky $260.00
Ayisha Mertens $160.00
第二个程序读取上面的文件作为它的数据文件,并且基于其内容打印工资单。例如,发出的第一张工资单应该是开出给Jim Baxter的$257.38.
#include<fstream> //文件流的需要
#include<cstdlib> //EXIT_FAILURE的定义
#include<iostream>
using namespace std;
//联系流和外部的文件名
#define inFile "EmpFile.txt" //员工文件
#define outFile "Salary.txt" //工资文件
//函数域
float processEmp(ifstream&,ofstream&);
int main()
{
ifstream eds; //输入:员工数据流
ofstream pds; //输出:工资数据流
float totalPayroll; //输出:总的工资
//准备的文件
eds.open(inFile);
if(eds.fail())
{
cerr<<"*** ERROR:Cannot open "<< inFile<<"for input."<<endl;
return EXIT_FAILURE; //错误返回
}
pds.open(outFile);
if(pds.fail())
{
cerr<<"***EROR: Cannot open "<<outFile<<" for output."<<endl;
eds.close();
return EXIT_FAILURE; //错误返回
}
//统计全部员工和计算全部工资
totalPayroll=processEmp(eds, pds);
//显示结果
cout<<"Total payroll is $"<<totalPayroll<<endl;
//关闭文件
eds.close();
pds.close();
return 0;
} //插入processEmp在这
float processEmp(ifstream& eds,ofstream& pds)
{
string firstName; //输入:员工的姓
string lastName; //输入:员工的名
float hours; //输入:工作时间
float rate; //输入:时间频率
float salary; //输出:总工资
float payroll; //返回值-总共公司的工资
payroll=0.0;
//读取第一个员工的数据报告
eds>>firstName>>lastName>>hours>>rate;
while(!eds.eof())
{
salary=hours*rate;
pds<<firstName<<" "<<lastName<<" "<<salary<<endl;
payroll+=salary;
//读取下一个员工的数据报告
eds>>firstName>>lastName>>hours>>rate;
}//结束while
return payroll;
}//结束processEmmp
在运行该程序之前,使用编辑器键入数据文件并将此文件保存在与程序相同的目录下。确定在键入最后一行后按下了RETURN键。
8.4关于读取字符串数据的更多信息
磅字符"#"作为每位员工名字的结束标志。可认为磅字符是字符串定界符。
空字符串:包含零个字符的空字符串。