第18章 输入与输出
第 18 章 输入/输出的信息管理 ——输入/输出流
对于输入数据和输出显示的知识,大家已经非常熟悉了,回想一下,在编程三部曲的输入这部曲中,最拿手的就是用 cin 语句从键盘向程序中输入信息(数字或字符),或用赋值语句向程序中输入信息;而在输出这部曲中,则常用 cout 语句把程序中的信息往显示器上输出。以上这些就是这一站的景点——输入/输出流(I/O 流)中的一部分内容。就像物流公司把物品从某处流向(运送到)另一处一样,计算机、C++中的信息也同样是一种物品,它也是需要流动、传送的,这样才能使程序、生产正常运转,让生活变得更丰富多彩。
虽然已经体验了输入数据和输出显示这部分内容,但读者也许在编写程序中还是有许多困惑。例如,读者已经编写过的几个学生的成绩管理程序,每运行一次都要重新把几个学生的所有成绩从键盘上输进去(假设用cin语句),这显得麻烦又耗时。如果管理一个学校几千名学生的成绩,每次运行程序时都全部从键盘输入,那会消耗大量的时间,还怎么去运行程序、管理成绩呢?所以单用cin语句输入信息显然不够用。
有些读者会说,在编程时干脆就用赋值语句对这些数据信息进行初始化赋值。其实,这也不实用,因为有些学生的成绩需要修改、添加、删除,还随时会面临着学生人数的增减,另外,有些学生的信息不正确,还要修改,而运行程序的人员一般都不会改程序,一般也不允许这些运行人员改程序,这样一来,下一次运行的程序还是原来未改动的程序,即信息未被修改,所以此法不实用。
那究竟该怎么办呢?大家知道,程序可以存储成文件,使用时把程序打开,就可以方便地运行了,而信息和程序在计算机中都是用计算机的机器语言存储的,那么,信息可否也像程序那样,用文件保存起来,供日后随意使用呢?C++的回答是:可以。这真是车到山前必有路,柳暗花明又一村。下面就一起来观赏吧。
18.1 例题详解
18.1.1 运用 I/O 文本文件实现字符串的写入与读出
【例 18-1】通过 C++ 的输入/输出文本文件,实现将字符串 “I am a C++ champion!” 写入文本文件,并将写入的文本内容读出,然后在屏幕上显示。
- 用心分析人想通,机随人行写提纲
同样是按照 “人定胜机,机随人行” 的法则编程。
人的做法是:
1)写入:找到一本记录本→打开记录本→写入该字符串→合上记录本。
2)读出:找到该记录本→打开记录本→读出该字符串→抄在一张纸上(显示出来)→
合上记录本。
2. 对号入座写文章,头脑运行改文章
根据上述提纲,结合 C++ 的文件 I/O 流语句,对号入座写文章——编写 C++ 源程序代码。
程序设计思路:
对应于上述提纲,记录本就是文件,打开记录本就是打开文件,合上记录本就是关闭文件,其余同提纲。
这是用文件 I/O 流的方法,通过文本文件对一个字符串进行操作,适合采用顺序结构。
首先,在主程序中调用写入文件子函数,实现将字符串 “I am a C++ champion!” 写入文本文件,然后在主程序中调用读出文件子函数,实现将字符串 “I am a C++ champion!” 的内容读出到缓冲区,再将读取的字符串输出到屏幕显示。
程序代码如下:
//ex18-1.cpp
#include<fstream.h>
#include<iostream.h>
#include<stdlib.h>
void ord(fstream f1) //判断文件是否能打开的子函数
{
if(!f1) //如果打开文件失败,该文件对象名返回0值,!0=1,
//然后就执行if中的这段复合语句
{
cerr<<"文件打不开"<<endl; //cerr 输出错误信息:文件打不开
exit(1); //退出
}
}
void ffout() //写入文件子函数
{
fstream f1("e:\\VCbook\\wf3.txt",ios::out); // fstream f1 是既定义了I/O流的对象 f1,
//又以写方式(ios::out)打开文本文件 wf3.txt,
//双引号内是文件路径(即文件夹)\\文件全名
ord(f1); //调用判断文件是否能打开的子函数
f1<<"I am a C++ champion! n"; //可以打开文件,就写入文本信息(字符串的内容)
//到文件中
f1.close(); //关闭文件
}
void ffint() //读出文件子函数
{
char a[100];
fstream f1("e:\\VCbook\\wf3.txt",ios::in); // fstream f1 是既定义了I/O流的对象 f1,
//又以读方式(ios::in)打开文本文件 wf3.txt
ord(f1); //调用判断文件是否能打开的子函数
}
while(!fl.eof())//当不是文件末尾时读出文本信息,
//eof 函数表示“文件末尾”,没到文件末尾时,fl.eof()=0,!0=1,继续循环(读);
//否则,结束循环
{
fl.getline(a,70); //用 I/O 流成员函数 getline 读取一行文本,并存入变量 a 中
cout<<a<<endl; //将读取到 a 中的一行文本输出到屏幕
}
fl.close();
void main()
{
ffout(); //调写入文件子函数
ffint(); //调读出文件子函数
}
将写出的程序与上述提纲分析比照,并用人脑跟踪运行,直至认为正确(即可以完成题目的功能,也符合C++的语法及规定),就可以转入下一步了。
3. C++帮助且裁判,人定胜机凯歌扬
将上面的程序输入计算机→编译→连接→如果无错→运行[→调试修改→如果无错→运行]。
上机实验记录如表18-1所示。
| 出错现象 | 出错原因 | 解决办法 |
| syntax error: missing"; before "< | 没“#” | 加“#”此错纠正 |
注意:使用完文件后,一定要关闭文件。
运行结果:
I am a C++ champion!
18.1.2 采用文件 I/O 流管理学生成绩档案
结果正确。
【例 18-2】请用文件 I/O 流的方法,对学生成绩档案进行管理。将学生成绩档案数据用二进制文件进行存储,包括学号、姓名、英语、计算机和中文成绩,并将其显示出来。
1. 用心分析人想通,机随人行写提纲
同样是按照 “人定胜机,机随人行” 的法则编程。
人的做法是:
1)写入:找到一本记录本→打开记录本→写入学生成绩档案信息→合上记录本。
2)读出:找到该记录本→打开记录本→读出学生成绩档案信息→抄在一张纸上(显示出来)→合上记录本。
2. 对号入座写文章,头脑运行改文章
根据上述提纲,结合 C++ 的文件 I/O 流语句,对号入座写文章——编写 C++ 源程序
代码。
程序设计思路:
这是用文件 I/O 流的方法对学生成绩档案进行管理,而且要用二进制文件存储,故也适合采用顺序结构。
首先,在主程序中打开文件 s6.dat,输入数据到结构变量 stu 中,并将数据写入文件 s6.dat 中,写完后,关闭文件;然后再打开文件 s6.dat,从中读出数据,并在屏幕上显示,读完后,再关闭文件。
程序代码如下:
#include<iostream.h>
#include<iomanip.h>
#include<fstream.h>
#include<stdlib.h>
#define MAX3 //自定义MAX代表课程的数目
#define NUM2 //自定义NUM代表学生的人数
struct student //声明结构 student
{
int id;//学号
char name[10]; //姓名
int score[MAX]; //成绩数组
};
void main()
{
long endpos;
student stu;//定义 student 这种结构类型的变量 stu
ofstream wr("e:\\VCbook\\mat3\\s6.dat", ios::binary | ios::app);
// ios::binary 表示以写方式打开二进制文件 s6.dat
// ios::app 表示将输出的内容追加到文件末尾
if(!wr) //判断文件是否能打开
{
cerr<<"文件打不开"<<endl;
exit(1);
}
for(int i=0; i<NUM; i++) //输入数据到 stu 中,并写入文件 s6.dat 中
{
cout<<"学号�";
cin>>stu.id;
cout<<"姓名";
cin>>stu.name;
cout<<"英语 计算机 中文成绩�";
for(int j=0; j<MAX; j++)
cin>>stu.score[j];
wr.write((char*)& stu.sizeof(student)); //写入数据到文件 s6.dat 中
}
wr.close(); //关闭文件
cout<<setiosflags(ios::left) //左对齐显示 stu 中的数据的字段名栏
}
<<setw(10)||<<"学号:"//每个字段的长度为10个字符
<<setw(10)||<"姓名:"
<<setw(10)||<"英语"
<<setw(10)||<"计算机"
<<setw(10)||<"中文成绩"<endl;
ifstream rr("e:\\VCbook\\mat3\\s6.dat",ios::binary);//以读方式打开二进制文件 s6.dat
if(!rr)
{
cerr<<"文件打不开"<endl;
exit(1);
}
rr.seekg(0,ios::end);//将读指针移到文件末尾
endpos=rr.tellg();//记录文件末尾位置
i=0;
rr.seekg(0,ios::beg);//将读指针移到文件头
while(rr.tellg()!=endpos)//读出文件 s6.dat 中的数据并左对齐显示,直到最后一条记录
{
rr.read((char*)& stu,sizeof(student));//从文件中读出数据
cout<<setw(10)||<stu.id
<<setw(10)||<stu.name;
for(int j=0;j<MAX;j++)
cout<<setw(10)||<stu.score[j];
cout<<endl;
}
rr.close();//关闭文件
将写出的程序与上述提纲分析比照,并用人脑跟踪运行,直至认为正确(即可以完成题目的功能,也符合C++的语法及规定),就可以转入下一步了。
3. C++帮助且裁判,人定胜机凯歌扬
将上面的程序输入计算机→编译→连接→如果无错→运行[→调试修改→如果无错→运行]。
运行结果(此次运行前该文本文件已写入了8位学生的数据):
学号:9
姓名:王五
英语 计算机 中文成绩:778899
学号:10
学号:10
姓名:孙六
英语 计算机 中文成绩:556677
| 学号: | 姓名: | 英语 | 计算机 | 中文成绩 |
| 1 | 张一 | 30 | 40 | 50 |
| 2 | 李二 | 0 | 40 | 50 |
| 3 | a | 71 | 81 | 91 |
| 4 | b | 72 | 82 | 92 |
| 5 | c | 83 | 93 | 100 |
| 6 | d | 65 | 75 | 85 |
| 7 | e | 76 | 86 | 96 |
| 0 | 8 | 88 | 78 | 98 |
| 9 | 王五 | 77 | 88 | 99 |
| 10 | 孙六 | 55 | 66 | 77 |
结果正确。
18.2 勤奋实践,总结评分
18.2.1 习题
1\. 将一段文本信息写入文本文件中,再将写入的信息读出,并显示读出的结果,请编程实现。
2.用二进制文件存储一些数据或信息,再读出这些被存储的数据或信息。请编写程序实现这些功能。
3.请对某个单位的职工工资数据进行读/写操作,其中,职工工资数据包括职工号、职工姓名、职工工资,并使用格式控制符打印输出这些数据,请编写实现上述功能的程序。
- 请将第3题的文件用文本文件存储。
- 在第3题的二进制文件的末尾添加一条新调入的职工的数据。
6.在第3题的职工工资文件中,假设某职工调动工作不在此单位了,请将该职工的数据删除。
- 在第3题的职工工资文件中,某职工增加了工资,请做修改,改完后要保存到文件中,请编写程序实现。
8.在第3题的职工工资文件中,只想显示最后一位职工的数据,请编写程序实现该功能。
9.在第3题中,把读、写函数写成子函数形式,并使其有判断文件是否能打开的功能,用主函数调用这些子函数完成第3题的工作。
10.对10个学生的简单档案进行文件存储,档案的数据项目包括学生姓名、学号、出生年月、性别、籍贯、政治面貌,请选择最佳的I/O流方式完成这项工作。
18.2.2 思考题
1.对第3题的职工工资数据进行插入、查询操作,然后用文件进行保存,并取出这些数据,请编写程序实现。
- 请编写程序,对例18-2中的学生成绩数据进行修改、删除等操作。
18.2.3 总结评分,继续提高
浏览、体验了例题、习题和思考题之后,说说自己的体会和收获,并认真总结,按表 18-2 给自己评判一下能得多少分。
| 成绩 | A | B | C | D | 不足的原因 | 改进的方法 | 备注 |
| C++知识 | 完全理解和会用 | 基本理解和会用 | 肤浅理解和会用 | 基本不理解和不会用 | |||
| 程序原创(独立编程)的比例(%) | ≥90 | 80~89 | 60~79 | < 60 |
不论得分多少,大家都要总结经验,扬长避短,勤奋努力,不断提高。
最后浏览一下本章的第4节,可以先考考自己,然后再浏览,以便使重点知识掌握得更牢固。
18.3 输入/输出流知识
C++这个物流公司是如何管理物流(I/O流)的呢?它有什么高超的本事,能把输入/输出信息管理得井井有条呢?C++对输入/输出信息的管理主要分为三大类:第一类,仓储式管理——文件I/O流;第二类,运输式管理——标准I/O流;第三类,临时内存管理——字符串I/O流。本节将介绍前两类,请认真观赏吧。
18.3.1 在什么情况下用到输入/输出流
第一类:文件 I/O 流(仓储式管理)。
当需要用文件保存管理信息,使其能像打开和关闭程序文件一样方便时用此。
例如,数据库类型的数据、文字等信息,如学生档案信息、学生成绩信息、仓库物品信息、商场商品信息等;一些特殊的要保存的信息,如身份证号码、密码等,用文件 I/O 流管理为好。
第二类:标准设备 I/O 流(运输式管理)。
当在指定的标准输入或输出设备中管理信息时用此。
例如,从标准输入设备(键盘)上输入信息的语句 cin,向标准输出设备(显示器)输出信息的语句 cout 等。
18.3.2 关于输入/输出流
1. 什么叫输入/输出流
输入/输出流是 C++程序对输入/输出信息的管理,这些管理包括对输入/输出流的设备、使用(如打开、关闭文件,读出或写入文件信息)等方面的管理,它主要分为三类,前面已叙述过。
2. 文件 I/O 流
(1) 文件是什么
把这个概念单提出来是不是小题大做了?读者对这个概念及其应用早已熟悉了,如Word文件,C++的源文件、头文件等。是的,这些都是文件,但光有C++的源文件、头文件这类程序文件,程序的运行还是不完善的,还应有数据信息文件。一切用数据说话,数据
文件相当于米,程序文件相当于巧妇,巧妇难为无米之炊啊!
有的读者会说,那以前单单有程序文件,这个巧妇不也可以“炊”得好吗?是的,但是要知道,那是因为在程序文件中有输入(信息数据)这部曲,所以巧妇能“炊”得好,但是在程序中输入的信息数据是不能保存下来的,当程序运行完毕,信息数据就消失了,也就产生了本章开头所提到的困惑。所以,要想程序能实际应用、完善应用,还需要用数据文件来支撑。
不论什么文件,都是保存在计算机的外存(外部存储器)中,如磁盘、硬盘、光盘、U盘等,这样才能在程序运行完毕或者断电后长期保存文件信息。
按文件信息的组织形式来分类,可分为 ASCII 码文件和二进制文件。ASCII 码文件也叫做文本文件(Text)或字符文件,二进制文件也称为内部格式文件或字节文件。
这两种文件的用途如下。
ASCII 码文件:当文件用于显示、打印或阅读时用此。
二进制文件:用于暂存,即经常要在程序运行时把数据保存进磁盘或从磁盘中取出来时用此。
(2)文件 I/O 流的定义和使用
这两种文件的使用没有绝对的界限,当两种用途均有,且某种用途多于另一种时,就采纳用途多的使用,或二者适时适地地使用。
通常情况下,在记录本上进行记录的步骤是:找到某本记录本→打开记录本→记录信息→合上记录本;在记录本上找出信息的步骤是:找到该本记录本→打开记录本→找出所要信息→合上记录本。类似地,可以把文件看成是记录本,因为文件就是用来存储信息的,与记录本的功能相似,只不过文件是通过计算机的操作来记录、管理信息而已。所以,文件的管理操作无外乎就是打开、关闭、读、写文件等。
C++对其管理的所有 I/O 流都定义了管理条例,这些管理条例分别声明在类中,所有 I/O 流的基类是 ios,其下有许多派生类,包括管理文件 I/O 流的派生类、声明这些派生类的头文件。
注意:要清楚输出(写)、输入(读)这些概念,它们均以计算机的内存为基准点。
写(到文件)=输出(内存→文件)。
读(出文件)=输入(内存←文件)。
下面来浏览和体验 C++ 是如何定义和使用文件 I/O 流的。
1)文件 I/O 流的定义。
这里的类还是第12章中讲的类,大家都知道,类是不能直接用类名来操作的,要定义类的对象,用对象来操作。前面已提到C++对文件I/O流的管理条例都已经制定在文件I/O流类中,那么,要对文件进行操作,也同样要定义这些类的对象,然后才能使用已制定的文件管理条例。所以,要先定义对象,再通过对象调用文件类的成员函数,然后才能对文件进行操作。另外,调用文件类的成员函数时还有一些规定,如文件是输入还是输出、怎样打开、是文本文件还是二进制文件等。归纳可知,定义文件I/O流有两种方式(通式)。
方式一:
类名 对象名://定义对象和引用对象成员的格式,与第12章中所讲的类的知识一样
对象名.成员函数名("文件名",[输入/输出方式][打开方式][二进制 binary]);
方式二(将方式一中的两句合二为一,一般多用此方式):
类名 对象名("文件名",[输入/输出方式][打开方式][二进制 binary]);
上述两件方式中,有以下几点需要注意。
① 类名主要用到以下3种。
ifstream——读文件时用此,此方式若不注明读方式,则取其默认方式:读方式 ios::in,见例 18-4 和例 18-5。
ofstream——写文件时用此,此方式若不注明写方式,则取其默认方式:写方式 ios::out 和 ios::trunc,见例 18-3 和例 18-6。
fstream——读、写或又读又写文件时都可以用此,但一定要注明读方式 ios::in 或写方式 ios::out 或二者同时存在方式 ios::in | ios::out,见例 18-7 和例 18-8。
这 3 个类名在头文件 fstream.h 中声明,程序中要加上声明类名的头文件的包括语句 “#include
②文件名包含了文件路径,转路径要用两个反斜杠(\\)表示。
③ 输入/输出方式和打开方式的定义如表 18-3 所示。文件名后若有选项,则应以“,”与第一个选项相隔,选项中若有多种方式,则用按位或的符号“|”分隔。
| 方式 | 作用 |
| ios::in | 读方式打开文件 |
| ios::out | 写方式打开文件(这是默认方式),如原来有名称为此的文件,则将原有内容全部删除 |
| ios::app | 写方式打开文件,在文件的末尾写入数据 |
| ios::ate | 打开已有的一个文件,文件指针指向文件末尾 |
| ios::trunc | 打开一个文件,如原来有名称为此的文件,则将原有内容全部删除;如文件不存在,则建立新文件 |
| ios::binary | 以二进制方式打开文件,如无此项,则默认是 ASCII 方式 |
| ios::nocreate | 打开一个已有的文件,如文件不存在,则打开失败,但不建立新文件 |
| ios::noreplace | 如果文件不存在,则建立新文件,否则操作失败,也不去更新原来的文件 |
| ios::in| ios::out | 读和写方式打开文件,文件可读又可写 |
| ios::out| ios::binary | 以二进制方式打开并写文件 |
| ios::in| ios::binary | 以二进制方式打开并读文件 |
ofstream out1:
out1.open("e:\\VCbook\\stul");//以写方式打开文本文件 stul.txt,因为没注明方式,
//所以以默认的写方式 ios::out 和方式 ios::trunc 打开文件
ord(out1); //调用判断文件是否能打开的子函数
【例18-4】
ifstream out2("e:\\VCbook\\stu1");//以读方式打开文本文件 stu1.txt,与例 18-3 同理,//以默认的读方式 ios::in 打开文件
【例18-5】
ifstream out2;
out2.open("e:\\VCbook\\p2.dat",ios::binary);//以读方式打开二进制文件 p2.dat
【例 18-6】
ofstream out1("e:\\VCbook\\p2.dat",ios::binary);//以写方式打开二进制文件 p2.dat
【例 18-7】
fstream f1("e:\\VCbook\\wfl.txt",ios::in|ios::out);//以读和写方式打开文本文件 wfl.txt,
//使用 fstream 必须指明以什么方式打开文件,此处同时指明两种方式,
//即读方式 ios::in 和写方式 ios::out
【例 18-8】
fstream wr("e:\VCbook\mat3\s6.dat",ios::in|ios::out|ios::binary|ios::app);
//以读和写方式打开二进制文件 s6.dat,
//并将数据追加到文件末尾
2)文件 I/O 流的使用。
已经司空见惯的输出语句 cout 和输入语句 cin,实际上是 ostream 等流类的对象,在 iostream.h 头文件中已做了声明,所以在程序中加上此头文件的包括语句 “#include
输入/输出的方法有以下几种(注意,除了已有的 I/O 流对象外,均要先用定义文件 I/O 流的两种方式之一定义对象名,再进行下述操作)。
方法一(文本文件和二进制文件均可用此法):文件 I/O 流的使用方法与 cin、cout 的使用方法相同,只不过对象名换了,不是 cin 和 cout 了。
输出(写文件)时用:
对象名<<要输出的信息或其存储单元:
【例18-9】
cout<<学习 C++";
对应例 18-3 有:
out1<<12;//写入 12 到文件中
对应例 18-7 有:
f1<<"I am a C++ champion! \n";//写入文本信息到文件
输入(读文件)时用:
对象名>>被输入的信息的存储单元:
【例 18-10】
cin>>y;
对应例 18-4 有:
out2>>a; //用 I/O 流成员对象 out2 读取文本文件 stul.txt 的内容 12 到内存变量 a 中
方法二:二进制文件主要用 read(输入)和 write(输出)成员函数。
用 read 和 write 成员函数读/写二进制文件的方法如下。
read 语句的定义及使用:
对象名.read((char*))& 被读信息的首地址, sizeof(首地址的数据类型));
write 语句的定义及使用:
对象名.write((char)& 写入信息的首地址, sizeof(首地址的数据类型));
其中, $ (char^{*}) $是将读/写的信息强制转换成字符指针,sizeof(首地址的数据类型)是一次读/写文件的字节数(也可用其他方式表达此项)。
如例 18-2 所示。
3)文件的关闭。
注意:写完、读完文件后一定要将其关闭。
关闭文件的格式为:
对象名.close();
【例18-11】
f1.close(); //关闭文件,f1 是文件对象名
4)判断文件是否能打开。
判断文件能否打开的方法有以下几种(注意,以下各种方法中均要有头文件包括语句“#include
方法一:
① if(!文件对象名)//如果打开文件失败,该文件对象名返回0值,因为!0=1,//所以就执行if中的复合语句
{
cerr<<"文件打不开"<endl;//cerr 输出错误信息:文件打不开
exit(1);//退出
}
void ord(ofstream f)
{
if(!f)//如果打开文件失败,该文件对象名 f 返回 0 值,因为!0=1,
//所以就执行 if 中的复合语句
{
cerr<<"文件打不开"<endl;//cerr 输出错误信息:文件打不开
}

方法二:
将方法一中的②句换为:
cout<<"文件打不开"<<endl;
方法三:
将方法一和方法二中的③句换为:
abort();
还有其他一些方法,此处不再一一介绍。
5)文件的随机访问。对文本文件的定位通常不准确,所以处理需要对文件准确定位的随机访问时,使用二进制文件较合适。它是靠文件中的一个文件指针来导引的。文件流中与文件指针关联的定位等成员函数如表18-4所示。
| 文件操作 | 成员函数 | 功能 |
| 读文件(输入) | tellg() | 返回读文件指针的当前位置 |
| seekg(在文件中的位置) | 将读文件指针移到指定的位置 | |
| seekg(位移量,参考位置) | 读文件指针以参考位置为基准,按位移量(字节数)移动 | |
| gcount() | 返回最后一次读文件时所读入的字节数 | |
| 写文件(输出) | tellp() | 返回写文件指针的当前位置 |
| seekp(在文件中的位置) | 将写文件指针移到指定的位置 | |
| seekp(位移量,参考位置) | 写文件指针以参考位置为基准,按位移量(字节数)移动 |
在表18-4中,有以下几点需要注意。
① 先区分文件操作是读(输入)还是写(输出),再选用对应的成员函数。
② 既可以读又可以写的文件,则可以不用区分。
③读的成员函数的末尾是g(get,除了gcount()外)。
④ 写的成员函数的末尾是 p (put)。
⑤ 参考位置(即文件中的参考点)有3个:
ios::beg:文件的开头处,这是默认的。
ios::cur:指针目前的位置。
ios::end:文件的末尾处。
⑥ 位移量是以参考位置为基准所移动的字节的数量,即移动多少字节。
表18-4中的成员函数的使用方法与前面讲的类的成员函数的使用方法一样,即:
I/O 流对象名.成员函数(实参);
下面是一些关于文件的随机访问的例子,假设已声明了输入流对象名为 $ \pi $,输出流对象名为 wr。
【例 18-13】把写指针移到文件开头。
wr.seekp(0,ios::beg);//因为参考位置已是开头处,所以位移量为0字节;//因为写指针意味着进行输出操作,所以用seek后带p的成员函数
long curpos3=wr.tellp();//返回了写(输出文件)指针的开头位置;因为 tellp()函数的//返回值为长整型,所以存放读位置值的变量 curpos3 用长整型
rr.seekg(-56,ios::end);//先把输入文件的指针移到倒数第56个字节的位置
long curpos=rr.tellg();//再返回该指针当前的位置值
【例 18-16】返回最后一次输入时的开始位置。
long endinput=rr.gcount();//返回最后一次输入所读入的字节数
rr.seekg(-endinput,ios::end);//从文件末尾起往后移动最后一次输入所读入的字节数
long curpos2=rr.tellg();//返回指针当前的位置值
【例 18-17】将输出文件的指针向前移动到第 84 个字节的位置,并返回该指针当前的位置值。
wr.seekp(84);//将输出文件的指针向前移动到第84个字节的位置
long curpos3=wr.tellp();//返回该指针当前的位置值
cout<<curpos3<<endl;//显示该指针当前的位置值
【例 18-18】当读入到第 112 个字节时,中间的 56 个字节的内容不要,移动读指针位置,并返回该指针当前的位置值。
if(long currun=rr.tellg()==112)//当读指针读入到第112个字节时
{
rr.seekg(56,ios::cur);//将读指针向前移56个字节
long inpos=rr.tellg();//返回该指针当前的位置值
cout<<inpos<<endl;//显示该指针当前的位置值
}
(3) 查看写入的文本文件内容
在调试程序或运行程序时,可以查看写入的文本文件内容,也可以大致看看二进制文件是否写入了一些乱码,来判断文件是否写入、是否正常。
查看写入的文本文件内容的方法有以下7种。
1)直接双击该文本文件的图标查看。
2)用 Windows 的记事本打开查看。
3)在 VisualC++6.0 中,打开文件查看。
4)用 Windows 的写字板打开查看。
5)在 Windows 中用其他打开文本文件的方式查看。
6)在 DOS 界面下,找到文件路径,输入 “type 全文件名”,然后按〈Enter〉键,即可查看。
7)用 C++ 编写程序来打开文件、读出文件内容并显示,见例 18-1 和例 18-2。用这种方法还可以查看二进制文件。
3. 标准 I/O 流
读者已熟练运用的 cout、cin 和这一章讲到的 cerr 都是 iostream.h 头文件中定义的标准 I/O 流对象。所谓标准,就是这些对象输入或输出的具体行为是指定好的,例如,cout 是将具体内容输出到屏幕,cin 是从键盘输入信息,并写入 cin 之后的变量中,cerr 是输出错误信息到屏幕。
还可用 get、getline、put 等输入、输出的公有成员函数提取和插入信息。
【例18-19】
c2=cin.get();//将从键盘读入的内容存放在变量 c2 中
cout.put(c2);//把变量 c2 的内容显示在屏幕上
对应例 18-1 有:
f1.getline(a,70); //用 I/O 流成员函数 getline 读取一行文本
18.3.3 如何运用输入/输出流
运用 I/O 流时,根据前面所讲述的知识,选用合适的 I/O 流方式,并正确书写语句,再加上调试和查看,一般就可以了。
下面举两个综合性的例子。
1. 关于文本信息的例子
【例 18-20】将 “我爱中国” 这段文字信息写入二进制数据文件中,然后读出该文件的内容,并显示在屏幕上。
(1)用心分析人想通,机随人行写提纲
同样是按照 “人定胜机,机随人行” 的法则编程。
人的做法是:
1)写入:找到一本记录本→打开记录本→写入“我爱中国”→合上记录本。
2)读出:找到该记录本→打开记录本→读出“我爱中国”→抄在一张纸上(显示出来)→合上记录本。
(2) 对号入座写文章,头脑运行改文章
根据上述提纲,结合 C++ 的文件 I/O 流语句,对号入座写文章——编写 C++ 源程序代码。
程序设计思路:
对应于上述提纲,记录本就是文件,打开记录本就是打开文件,合上记录本就是关闭文件,其余同提纲。
这是用文件 I/O 流的方法,通过二进制文件对一个字符串进行操作,适合采用顺序结构。
首先,在主程序中调用写入文件子函数,实现将字符串“我爱中国”写入二进制文件,然后在主程序中调用读出文件子函数,实现将字符串“我爱中国”的内容读出到缓冲区,再将读取的字符串输出到屏幕进行显示。
程序代码如下:
//ex18-10.cpp
#include<fstream.h>
#include<iostream.h>
#include<stdlib.h>
void ord(fstream f1)//判断文件是否能打开的子函数
{
if(!f1)
{
cerr<<"文件打不开"<<endl;
exit(1);
}
}
void ffout()//写入文件子函数
{
fstream f1("e:\\VCbook\\wf2.dat",ios::out|ios::binary|ios::ate|ios::trunc);
//以写方式打开二进制文件 wf2.dat, ios::ate 表示文件指针指向文件末尾;
//ios::trunc 表示将原有内容全部删除; 如文件不存在, 就建立新文件
ord(f1);//调用判断文件是否能打开的子函数
f1<<"我爱中国!\\n";//写入文本信息到文件
f1.close();//关闭文件
}
void ffint()//读出文件子函数
{
char a[100];
fstream f1("e:\\VCbook\\wf2.dat",ios::in|ios::binary);
//以读方式打开二进制文件 wf2.dat
ord(f1);//调用判断文件是否能打开的子函数
while(!f1.eof())//当不是文件末尾时读出文本信息
{
f1.getline(a,70);//用 I/O 流成员函数 getline 读取一行文本
cout<<a<<endl;//将读取的一行文本输出到屏幕
}
f1.close();
}
void main()
{
ffout();//调用写入文件子函数
ffint();//调用读出文件子函数
}
将写出的程序与上述提纲分析比照,并用人脑跟踪运行,直至认为正确(即可以完成题
目的功能,也符合 C++ 的语法及规定),就可以转入下一步了。
(3)C++帮助且裁判,人定胜机凯歌扬
将上面的程序输入计算机→编译→连接→如果无错→运行[→调试修改→如果无错→运行]。
运行结果:
我爱中国!
结果正确。
2. 关于数字信息的例子
【例 18-21】在主函数中对一个数组赋值,在子函数中将该数组信息写入二进制数据文件中,并以带参数的方式传递数组信息,然后再用另一个子函数从二进制文件中读出其内容,并显示在屏幕上。
(1)用心分析人想通,机随人行写提纲
同样是按照 “人定胜机,机随人行” 的法则编程。
人的做法是:
1)在主函数中声明一个数组并赋值→调用写入文件子函数,并将该数组信息以实参方式传递给写入文件子函数→再调用读出文件子函数。
2)写入文件子函数:找到一本记录本→打开记录本→写入该数组信息→合上记录本。
(2)对号入座写文章,头脑运行改文章
3)读出文件子函数:找到该记录本→打开记录本→读出该数组信息→抄在一张纸上(显示出来)→合上记录本。
根据上述提纲,结合 C++ 的文件 I/O 流语句,对号入座写文章——编写 C++ 源程序代码。
程序设计思路:
这是用文件 I/O 流的方法,通过二进制文件对一个数组进行操作,适合采用顺序结构。
程序代码如下:
//ex18-18.cpp
#include <iostream>
#include <fstream>
#include <fstream>
using namespace std;
/#include
#define N 10
void fread()//文件数据读取
{
ifstream infile("date12.dat", ios::in|ios::binary);
if(!infile)
cout<<"error"<<endl;
exit(1);
}
int b[N];//定义存放读出的文件数据的数组 b
for (int i=0;i<N;i++)
{
infile>>b[i];//将文件数据一个个地读出,并存放在数组的对应元素中
}
infile.close();
for (int j=0;j<N;j++)
{
cout<<b[j]<<endl;
}
void fwrite(int a[])//文件数据写入
{
fstream outfile("date12.dat",ios::out[ios::binary]);
if(!outfile)
{
cout<<"error"<<endl;
exit(1);
}
for(int i=0;i<N;i++)
outfile<<a[i]<<"";//将数组 a 中的元素一个个地写入到文件中
outfile.close();
}
int main()//主函数
{
int a[N];
for(int i=0;i<N;i++)//给数组 a 赋值
a[i]=10*i;
fwrite(a);//带着实际参数(数组 a)调用写入文件子函数
fread();//调用读出文件子函数
return 0;
}
将写出的程序与上述提纲分析比照,并用人脑跟踪运行,直至认为正确(即可以完成题目的功能,也符合C++的语法及规定),就可以转入下一步了。
(3)C++帮助且裁判,人定胜机凯歌扬
将上面的程序输入计算机→编译→连接→如果无错→运行[→调试修改→如果无错→运行]。
运行结果:
0
10
20
30
40
50
60
70
80
90
结果正确。
此题若用 read 和 write 成员函数编写程序,又该如何进行呢?请读者自行设计。
关于二进制文件的例子还可参考例 18-2 和例 20-2。
18.4 输入/输出流的亮点
输入/输出流是 C++ 程序对输入/输出信息的管理。
1. 在什么情况下用输入/输出流的亮点
第一类:文件 I/O 流(仓储式管理)。当需要用文件保存管理信息,使其能像打开和关闭程序文件一样方便时用此。
第二类:标准 I/O 流(运输式管理)。当在指定的标准输入或输出设备中管理信息时用此。
2. 输入/输出流具体知识的亮点
1)文件是能长期保存信息的,它保存在计算机的外部存储器中。
文件分为文本文件和二进制文件,文本文件多用于显示、阅读、打印等直观输出,二进制文件多用于暂存,可根据实际情况对二者进行适当选用。
2)使用文件时的步骤:定义→打开→使用(读或写)→关闭。
定义和打开文件这两步可以分开,也可以合二为一。
要清楚输出(写)、输入(读)这些概念,它们均以计算机的内存为基准点。
写(到文件)=输出(内存→文件)。
读(出文件)=输入(内存←文件)。
文件 I/O 流的定义和打开一般多用合二为一的方式:
类名 对象名("文件名",[输入/输出方式][打开方式][二进制 binary]);
类名主要用到以下三种。
ifstream——读文件时用此。
ofstream——写文件时用此。
fstream——读、写或又读又写文件时都可以用此。
对于文件 I/O 流,输入/输出的方法有以下几种。
方法一(文本文件和二进制文件均可用此法):文件 I/O 流的使用方法与 cin、cout 的使用方法相同,只不过对象名换了,不是 cin 和 cout 了。
输出(写文件)时用:
对象名<<要输出的信息或其存储单元:
输入(读文件)时用:
对象名>>被输入的信息的存储单元:
方法二:二进制文件主要用 read(输入)和 write(输出)成员函数。
用 read 和 write 成员函数读/写二进制文件的方法如下。
read 语句的定义及使用:
对象名.read((char)& 被读信息的首地址, sizeof(首地址的数据类型));
write 语句的定义及使用:
对象名.write((char)& 写入信息的首地址,sizeof(首地址的数据类型));
写完、读完文件后一定要将其关闭,关闭文件的格式为:
对象名.close();
3)标准 I/O 流不能保存信息,即只是输入、输出到指定的设备上,并没有以文件的形式保存在计算机的外部存储器中,例如,cin——从键盘输入,cout——输出到屏幕。
3. 运用输入/输出流的亮点
1)首先,按照题述的任务要求进行规划设计,采用合适的 I/O 流方式,并规划好在程序的何处设置输入、输出。
例如,若采用文件 I/O 流方式,当添加了数据或修改了数据时,就应更新文件,即要写入文件;当要显示、打印时,就应从文件中读出信息。
2)按各种 I/O 流的格式,正确书写和使用。
3)调试中还可以查看文件,以帮助确认程序的对错。