第15章 输入输出与文件第四篇 输入输出处理
第15章 输入输出与文件
(本资料qq讨论群112133686)在C语言中,用printf()和scanf()进行输入输出,往往不能保证所输入输出的数据是可靠的、安全的。C++语言的输入输出优于C语言中的printf()和scanf(),C++通过输入输出I/O类库来实现丰富的I/O功能,但是比较复杂有许多细节需要掌握。C++的输入输出流是指由若干字节组成的字节序列,这些字节中的数据按顺序从一个对象传送到另一对象。流表示了信息从源到目的端的流动。在C++中,输入输出流被定义为类,C++的I/O库中的类称为流类(stream class)。用流类定义的对象称为流对象。cout和cin并不是C++语言中提供的语句,它们是iostream类的对象,C++对文件的输入输出需要用ifstream和ofstream类,ifstream支持对文件的输入操作,ofstream支持对文件的输出操作。
本章举的实例都是C++输入输出相关的例子,如怎样用流类库输出一个文件、对二进制文件读写(和文本文件读写)、获取文件长度和随机写入数据,以及输入输出库函数例子,通过这些实例让读者加深对输入输出的理解。本章重点是文件输入和输出的实例,这些对于初学者反复多实践,加深理解,举一反三。
15.1 输入输出概述
案例15-1 使用流类库输出一个文件
【案例描述】
本章开始演示C++输入输出系统的实例,程序的输入指的是在输入文件中把数据传送给程序,程序的输出指的是从程序把数据传送给输出文件。本实例创建个文件,写入数据,然后读取显示。本例效果如图15-1所示。
【实现过程】
将“Hello world!”字符串输出到“example.txt”文本文件中。以生成的“example.txt”文件作为输入文件,通过两次fin >> str;,读取内容,并在屏幕上输出。代码如下:
#include<iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
cout <<"流类库输出文件演示:"<<endl;
ofstream fout("example.txt"); //磁盘文件的输出
if(!fout) //不能打开文件
{ cerr << "打开文件错误!" << endl; //输出出错信息
exit(1); //结束程序
}
fout << "Hello world!"<< endl; //写入文件内容
fout.close(); //关闭磁盘文件
ifstream fin("example.txt"); //磁盘文件的输入
if(!fin) { //不能打开文件
cerr << "打开文件错误!" << endl;
exit(1);}
string str; //定义string类字符串
fin >> str; //读取文件内容
cout << str << " ";
fin >> str; //读取文件内容
cout << str << endl; //打印输出内容
system("pause");
return 0;
}
【案例分析】
(1)ostream类提供了格式化和无格式化的输出功能。包括:
— 用插入操作符(<<)输出标准类型数据;
— 用put成员函数输出字符;
— 用write成员函数实现无格式输出。
最常用的输出方法是在cout上用插入操作符(<<),插入操作符可以接受任何标准类型的实参,包括const char *、标准库string、complex等类型。实参可以是任何表达式包括函数调用,只要其结果是能被插入操作符能接受的数据类型即可。
(2)C++中的I/O是以流(stream)的形式出现的。
流的输入操作是字节从外部设备(包括键盘、磁盘、网络连接)输入到内存,是字节从设备到内存的流动。输出操作是从内存输出到外部设备(如显示器、打印机、磁盘、网络连接),是字节从内存到外部设备的流动。
提示:流实际上是一个处于传输状态的字节序列,是字节在对象之间的“流动”,流的操作包括输入与输出。
案例15-2 如何获得文件长度
【案例描述】
实际编程中常需要知道一个文件内容长度,如读取图像数据时知道文件有多长,以便初始化缓冲区。本例演示输入一个文件求文件长度的功能,效果如图15-2所示。
图15-2 如何获得文件长度
【实现过程】
打开一个文件example.txt,取得文件头的位置begin和取得文件尾的位置end,它们的差值(end-begin)就是文件的长度。代码如下:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
long begin,end;
ifstream myfile ("example.txt"); //磁盘文件的输入
begin = myfile.tellg(); //返回读指针相对于文件头的位置
myfile.seekg (0, ios::end); //输出文件中的指针从文件尾
end = myfile.tellg(); //返回读指针相对于文件头的位置
myfile.close(); //关闭磁盘文件
cout << "example.txt size is: " << (end-begin) << " bytes.\n";
return 0;
}
【案例分析】
(1)上述代码long tellg()返回读指针相对于文件头的位置。
(2)stream& seekg(long off, ios::seek_dir dir ),设定读取指针到距离文件某一特定位置off个字节的位置。特定位置由dir确定,dir为ios::beg、ios::cur、ios::end之一。当特定位置为ios::end时,off应为负数。
案例15-3 使用system()函数使屏幕停止
【案例描述】
如果调试控制台程序中,很多时候点击“启动调试”后是一闪而过,此时可有两种方法让cmd下dos调试屏幕暂停,一个是代码加上system("pause"),对这个读者很熟悉;另一个是在主函数main()的return 0前加上两句:cin.get()。本例效果如图15-3所示。
图15-3 使用system()函数使屏幕停止
【实现过程】
定义一个函数simon(),输入的是整数,主函数中输入整数,调用simon()显示输入整数,cin.get()屏幕暂停,让程序等待键击。代码如下:
#include <tchar.h>
#include<iostream>
void simon(int);
using namespace std; //这是一个使用std;名称空间的例子
int _tmain(int argc, _TCHAR* argv[])
{
simon(18);
cout<<"请输入一个整数: ";
int count;
cin>>count; //输入取得的整数
simon(count); //显示输入整数
cout<<"完成!"<<endl;
cin.get(); //这两行是使调试屏幕暂停,不会一闪而过
cin.get(); //让程序等待键击
return 0; //退出主函数
}
void simon(int n)
{
cout<<"现在整数是: "<<n<<" 测试!"<<endl;
}
【案例分析】
(1)代码中调用了一个getch()库函数,程序中的作用使屏幕暂停,当程序执行到此函数时就暂停,等待输入一个任意字符后,程序继续向下执行。这样做的好处是,可以使用户看清封面和主子菜单。
(2)cin.get(字符数组名,接收字符数目)用来接收一行字符串,可以接收空格。
提示:在VC++下,cin.get()如果不包含using namespace std指令,那么必须使用std::前缀std::cin.get。
15.2 高层I/O
案例15-4 读写二进制文件
【案例描述】
二进制文件是指含ASCII码字符外的数据的文件,它不能由文本编辑软件打开。在实际应用中,大多数文件都是二进制文件,如图像文件、影像文件等。这是一个二进制文件读写的例子,效果如图15-4所示。
图15-4 读写二进制文件
【实现过程】
创建ofstream流对象out对应于二进制文件example.asc,打开文件,以二进制形式写入浮点数字符数组fnum,以二进制形式按格式fnum读出数据并显示读出数据。代码如下:
#include <iostream>
#include <fstream>
using namespace std;
int main(void){
float fnum[4] = {11.22, -33.44, 55.66, 77.88};//定义浮点数字符数组
int i;
//创建ofstream流对象out
ofstream out("example.asc", ios::out | ios::binary);
if(!out){ //不能打开文件
cout << "Cannot open file.";
exit (1); //结束程序
}
out.write((char *) &fnum, sizeof(fnum)); //以二进制形式写入numbers.asc中
out.close(); //关闭流
for (i=0; i<4; i++)
fnum[i] = 0.0;
//创建ifstream流对象in,以便从numbers.asc中读取信息
ifstream in("example.asc", ios::in | ios::binary);
if(!in) { //不能打开文件
cout << "Cannot open file.";
exit (1); //结束程序
}
in.read((char *) &fnum, sizeof(fnum)); //以二进制形式读入数据
cout << in.gcount() << " bytes read." << endl; //显示读入数据字符数
for (i=0; i<4; i++)
cout << fnum[i] << " "; //标准输出,屏幕显示
in.close(); //关闭流
system("pause");
}
【案例分析】
(1)在代码中,二进制文件example.asc的读写利用istream类的成员函数read和write来实现。这两个成员函数的原型为:
istream& read(char *buffer,int len);
ostream& write(const char * buffer,int len);
字符指针buffer指向内存中一段存储空间,len是读写字节数。调用的方式如:
a. write(p1,50); b. read(p2,30);。
(2)输出二进制文件的方法是使用write()成员函数。输入二进制文件使用成员函数read()每次读取固定长度的数据,同样用eof()判断是否到达文件尾。
提示:C++语言把每个文件看成是一个有序的字节流。文件打开时,就创建一个对象,并将这个对象和某个流关联起来。包含<iostream>时,会自动生成cin、cout、cerr和clog这四个对象,与这些对象关联的流提供与文件通信的方法(文件操作)。
案例15-5 读写记事本
【案例描述】
文本文件是以ASCII表示的文件如记事本。在文件操作前,需要将程序与被操作的文件联系起来,使程序可以“引用”文件。在程序内定义一个文件类的对象,由该对象与文件发生联系,程序内所有的与文件的操作都是对该对象的操作。本实例举文本文件读写操作,效果如图15-5所示。
【实现过程】
键盘读入一行字符再输出到“example.txt”文本文件中。以生成的“example.txt”文件作为输入文件,通过两次infile>>line;,读取内容,并在屏幕上输出。代码如下:
#include <iostream>
#include <fstream>
#include <fstream>
#include <string>
using namespace std;
void main() {
char line[180];
fstream myfile; //建立文件流
myfile.open("example.txt", ios::out); //定义文件流对象,打开磁盘文件
if(!myfile) { //如果打开失败
cerr<<"File open or create error!"<<endl;
exit(1);
}
cin.getline(line,80); //从键盘读入一行字符
myfile<<line; //向磁盘文件输出数据
myfile.close(); //关闭磁盘文件
ifstream infile; //定义输入文件流对象
infile.open("example.txt"); //以输入方式打开磁盘文件
if (infile.is_open())
{
while (! infile.eof() )
{
infile>>line; //从磁盘文件读入
cout <<"读取文件内容为:"<<line << endl; //在显示器上显示
}
infile.close(); //关闭磁盘文件
}else //如果打开失败
cout << "Unable to open file";
system("pause");
}
【案例分析】
(1)C++的文件I/O模式分为两种文本模式与二进制模式,默认模式为文本模式。当使用文本模式时,输出到文件的内容为ASCII码字符(包括回车、换行)。也就是说,文本文件中只能存储ASCII码字