文件基本概念和文件流类
文件的概念
在计算机中,通常将一些内容相关的信息组织起来保存在一起并用一个名字标识,这就是文件。
C++根据文件数据的编码方式不同分为文本文件和二进制文件。
C++文件流类
流是一个逻辑概念,是对所有外部设备的逻辑抽象。
C++标准库中有3个流类可以用于文件操作。
- ifstream:用于从文件中读取数据
- ofstream:用于向文件中写入数据
- fstream:既可用于从文件中读取数据,又可用于向文件中写入数据。
打开和关闭文件
打开文件
在对文件进行读写操作之前,先要打开文件,打开文件有两个目的
- 建立关联;
- 指明文件的使用方式和文件格式。
打开文件的方式有两种
- 先建立流对象,然后调用open()函数连接外部文件
流类名 对象名;
对象名.open(文件名,模式);
- 调用流类带参数的构造函数,在建立流对象的同时连接外部文件。
流类名 对象名(文件名,模式);
表8-1 文件打开模式标记
模式标记 | 适用对象 | 作用 |
---|---|---|
ios::in | ifstream/fstream | 以读方式打开文件。如果文件不存在,则打开出错 |
ios::out | ofstream/fstream | 以写方式打开文件。如果文件不存在,则新建该文件;如果文件已经存在,则打开时清除原来的内容 |
ios::app | ofstream | 以追加方式打开文件,用于在文件尾部添加数据。如果文件不存在,则新建该文件 |
ios::ate | ofstream | 打开一个已有的文件,并将文件读指针指向文件末尾。如果文件不存在,则打开出错。 |
ios::trunc | ofstream | 删除文件现有内容。单独使用时与ios::out相同 |
ios::binary | ifstream/ofstream/fstream | 以二进制方式打开文件。若不指定此模式,则以默认的文本模式打开文件 |
ios::in | ios::out | fstream/ofstream | 打开已存在的文件,即可读取其内容,也可向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。 |
ios::in |ios::out|ios::trunc | fstream | 打开文件,即可读取其内容,也可向其写入数据。如果文件本来就存在,则打开时清除原来的内容,如果不存在,则新建。 |
例如,要从data.txt中读取数据
ifstream inFIle;
inFile.open("data.txt",ios::in);
或
ifstream inFIle("data.txt",ios::in);
关闭文件
使用fstream中的成员函数close()关闭文件。
程序8-1 建立流对象,然后调用open()函数连接外部文件
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile;
inFile.open("d:\\tmp\\test.txt",ios::in);
if(inFile)
{
cout<<"成功打开文件:d:\\tmp\\test.txt\n";
inFile.close();
}
else
cout<<"打开文件失败:d:\\tmp\\test.txt\n";
ofstream outFile;
outFile.open("test1.txt",ios::out);
if(!outFile)
cout<<"error1"<<endl;
else
{
cout<<"成功打开文件:test1.txt\n";
outFile.close();
}
outFile.open("tmp\\test2.txt",ios::out|ios::in);
if(outFile)
{
cout<<"成功打开文件:tmp\\test2.txt\n";
outFile.close();
}
else
cout<<"error2"<<endl;
fstream ioFile;
ioFile.open("..\\test3.txt",ios::out|ios::in|ios::trunc);
if(!ioFile)
cout<<"error3"<<endl;
else
{
cout<<"成功打开文件: ..\\test3.txt\n"<<endl;
ioFile.close();
}
return 0;
}
程序8-2 调用流类带参数的构造函数打开文件
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile("d:\\tmp\\test.txt",ios::in);
if(inFile)
{
cout<<"成功打开文件:d:\\tmp\\test.txt\n";
inFile.close();
}
else
cout<<"打开文件失败: d:\\tmp\\test.txt\n";
ofstream outFile("test1.txt",ios::out);
if(!outFile)
{
cout<<"error1"<<endl;
}
else
{
cout<<"成功打开文件: test1.txt\n";
outFile.close();
}
fstream outFile2("tmp\\test2.txt",ios::out|ios::in);
if(outFile)
{
cout<<"成功打开文件: tmp\\test2.txt\n";
outFile.close();
}
else
cout<<"error2"<<endl;
return 0;
}
文件读写操作
读写文本文件
C++将文件看成顺序排列的无结构的字节流。
程序8-3 对文本文件score.txt进行输入/输出
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
char id[11],name[21];
int score;
ofstream outFile;
outFile.open("score.txt",ios::out);
if(!outFile)
{
cout<<"创建文件失败"<<endl;
return 0;
}
cout<<"请输入:学号 姓名 成绩(以Ctrl+Z结束输入)\n";
while(cin>>id>>name>>score)
outFile<<id<<" "<<name<<" "<<score<<endl;
outFile.close();
return 0;
}
输入如下:
image.png
内容如下
image.png
程序8-4 读入学生成绩文件score.txt并显示内容
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
int main()
{
char id[11],name[21];
int score;
ifstream inFile;
inFile.open("score.txt",ios::in);
if(!inFile)
{
cout<<"打开文件失败"<<endl;
return 0;
}
cout<<"学生学号\t 姓名\t\t 成绩\n";
while(inFile>>id>>name>>score)
cout<<left<<setw(10)<<id<<" "<<setw(20)<<name<<" "<<setw(3)<<right<<score<<endl;
inFile.close();
return 0;
}
学生学号 姓名 成绩
20180001 zhangsan 90
2018001002 lisi 9
2018001003 wangwu 85
2018001004 zhangsanfeng 100
2008001005 zhouwuzhengwangyishi 100
程序8-5 读入文本文件,加上行号后显示内容
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
int main()
{
char ch,filename[20];
int count=0;
bool newline = true;
cout<<"请输入文件名: ";
cin>>filename;
ifstream inFile(filename,ios::in);
if(!inFile)
{
cout<<"打开文件失败"<<endl;
return 0;
}
while((ch=inFile.get())!=EOF)
{
if(newline)
{
cout<<setw(4)<<++count<<':';
newline=false;
}
if(ch=='\n')
newline=true;
cout<<ch;
}
inFile.close();
return 0;
}
请输入文件名: score.txt
1:20180001 zhangsan 90
2:2018001002 lisi 9
3:2018001003 wangwu 85
4:2018001004 zhangsanfeng 100
5:2008001005 zhouwuzhengwangyishi 100
程序8-6 对文本文件的内容排序,将结果输出到另一个文件
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
const int MAX_NUM = 1000;
class CStudent
{
public:
char id[11]; //学号。为10位,以'\o'结尾
char name[21]; //姓名。不超过20个字符
int score; //成绩
}stu[MAX_NUM]; //存放文件中读入的整数
int MyCompare(const void * e1,const void * e2)
{
return (*(CStudent *)e1).score-(*(CStudent *)e2).score;
}
int main()
{
ifstream srcFile("score.txt",ios::in);
if(!srcFile)
{
cout<<"opening source file error."<<endl;
return 0;
}
ofstream destFile("out.txt",ios::out);
if(!destFile)
{
srcFile.close();
cout<<"opening destionation file error."<<endl;
return 0;
}
int n = 0;
cout<<"排序前:\n";
while(srcFile>>stu[n].id>>stu[n].name>>stu[n].score)
{
cout<<stu[n].id<<" "<<stu[n].name<<" "<<stu[n].score<<endl;
n++;
}
qsort(stu,n,sizeof(CStudent),MyCompare);
cout<<"排序后: \n";
for(int i=0;i<n;++i)
{
cout<<stu[i].id<<" "<<stu[i].name<<" "<<stu[i].score<<endl;
destFile<<stu[i].id<<" "<<stu[i].name<<" "<<stu[i].score<<endl;
}
destFile.close();
srcFile.close();
return 0;
}
排序前:
20180001 zhangsan 90
2018001002 lisi 9
2018001003 wangwu 85
2018001004 zhangsanfeng 100
2008001005 zhouwuzhengwangyishi 100
排序后:
2018001002 lisi 9
2018001003 wangwu 85
20180001 zhangsan 90
2008001005 zhouwuzhengwangyishi 100
2018001004 zhangsanfeng 100
读写二进制文件
文本文件在存储数据时是以ASCII码形式保存数据的。二进制数据文件以基本类型数据的二进制形式存放。
C++用binary方式打开二进制文件,调用ifstream或fstream的read()成员函数从文件中读取数据,调用ofstream或fstream的write()成员函数向文件中写入数据。
- 用ostream::write()成员函数写文件
ostream和fstream的write()成员函数继承自ostream类。
ostream & write(char *buffer,int nCount);
程序8-7 用二进制文件保存学生信息
#include <iostream>
#include <fstream>
using namespace std;
class CStudent
{
public:
char id[11];
char name[21];
int score;
};
int main()
{
CStudent stu;
ofstream outFile("students.dat",ios::out|ios::binary);
if(!outFile)
{
cout<<"创建文件失败"<<endl;
return 0;
}
cout<<"请输入:学号 姓名 成绩(以Ctrl+Z结束输入)\n";
while(cin>>stu.id>>stu.name>>stu.score)
outFile.write((char *)&stu,sizeof(stu));
outFile.close();
return 0;
}
程序8-8 向二进制文件中追加数据
#include <iostream>
#include <fstream>
using namespace std;
class CStudent
{
public:
char id[11];
char name[21];
int score;
};
int main()
{
char ch;
cout<<"确实需要向文件中追加新数据吗(Y/N)?";
cin>>ch;
if(ch=='Y' || ch=='y')
{
CStudent stu;
ofstream outFile("students.dat",ios::app|ios::binary);
if(!outFile)
{
cout<<"以追加方式打开文件失败"<<endl;
return 0;
}
cout<<"请输入: 学号 姓名 成绩(以Ctrl+Z结束输入)\n";
while(cin>>stu.id>>stu.name>>stu.score)
outFile.write((char*)&stu,sizeof(stu));
outFile.close();
}
return 0;
}
- 用istream::read()成员函数读文件
继承自类istream
istream &read(char * buffer,int nCount);
- 用ostream::gcount()成员函数得到读取字节数
int gcount();
程序8-9 从二进制文件中读取数据
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
class CStudent
{
public:
char id[11];
char name[21];
int score;
};
int main()
{
CStudent stu;
int count=0,nbyte=0;
ifstream inFile("students.dat",ios::in|ios::binary);
if(!inFile)
{
cout<<"创建文件失败"<<endl;
return 0;
}
cout<<"学生学号 姓名\t\t\t成绩\n";
while(inFile.read((char *)&stu,sizeof(stu)))
{
cout<<left<<setw(10)<<stu.id<<" "<<setw(20)<<stu.name<<" "<<setw(3)<<right<<stu.score<<endl;
count++;
nbyte += inFile.gcount();
}
cout<<"共有记录数: "<<count<<",字节数: "<<nbyte<<endl;
inFile.close();
return 0;
}
学生学号 姓名 成绩
2019001001 zhangsan 90
2019001002 LiSi 100
2019001003 WangWu 78
2008001005 xuexiansheng 102
2011002004 qixiansheng 100
共有记录数: 5,字节数: 180
用成员函数put()和get()读写文件
可以用类ifstream和类fstream的成员函数get()从文件中一次读取一个字节,也可以用类ofstream和类fstream的成员函数put()向文件中一次写入一个字节。
函数get()有3中主要形式
- int get();
- istream& get(char &rch);
- istream & get(char *pch,int nCount,char delim='\n');
函数put()语法格式
ostream &put(char ch);
程序8-10 文件复制
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc,char * argv[])
{
if(argc != 3) //判断main的参数是否位3个
{
cout<<"使用方法错误。正确的命令格式:mycopy SourceFile NewFile"<<endl;
return 0;
}
ifstream inFile(argv[1],ios::binary|ios::in);
if(!inFile)
{
cout<<"Source file open error."<<endl;
return 0;
}
ofstream outFile(argv[2],ios::binary|ios::out);
if(!outFile)
{
cout<<"New file open error."<<endl;
inFile.close();
return 0;
}
char c;
while(inFile.get(c))
outFile.put(c);
outFile.close();
inFile.close();
return 0;
}
随机访问文件
如果一个文件只能进行顺序存取操作,称为顺序文件。典型的是键盘、显示器和保存在磁带上的文件。
如果一个文件可以在文件的任意位置进行存取操作,称为随机文件。磁盘文件就是随机文件。
在访问文件的过程中,若严格按照数据保存的次序从头到尾访问文件,则称为顺序访问。
若不必按照数据的存储次序访问文件,而是要根据需要在文件的不同位置进行访问,则称为随机访问。
在C++中,每个流都有一个”流指针“,由系统控制,会随着对流的各种操作在字节流中移动。
在类istream、类ifstream和类fstream中有成员函数seekg(),可以设置文件读指针的位置;
在类ostream、类ofstream和类fstream中有成员函数seekp(),可以设置文件的写指针位置。
类istream中与位置指针相关的函数
-
移动读指针函数
istream & seekg(long pos);将读指针设置为pos。
istream & seekg(long offset,ios::seek_dir dir); 将读指针按照seek_dir的指示移动offset个字节。 -
返回读指针当前位置值的函数
long tellg(); 返回值为流中读指针的当前位置。
类ostream中与位置指针相关的函数
-
移动写指针函数
ostream & seekp(long pos);
ostream & seekp(long offset,ios::seek_dir dir); -
返回写指针当前位置的函数
long tellp();
程序8-11 修改程序8-7,
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
class CStudent
{
public:
char id[11];
char name[21];
int score;
};
int main()
{
CStudent stu;
int count = 0,nbyte=0;
ifstream inFile("students.dat",ios::in|ios::binary);
if(!inFile)
{
cout<<"创建文件失败"<<endl;
return 0;
}
else
{
cout<<"打开文件时位置指针: "<<inFile.tellg()<<endl;
cout<<" 每个记录大小: "<<sizeof(CStudent)<<endl;
}
cout<<"学生学号 姓名\t\t\t成绩\t流指针";
while(inFile.read((char *)&stu,sizeof(CStudent)))
{
cout<<left<<setw(10)<<stu.id<<" "<<setw(20)<<stu.name<<" "<<setw(3)<<right<<stu.score<<"\t"<<inFile.tellg()<<endl;
count++;
nbyte += inFile.gcount();
}
cout<<"读取文件结束时位置指针: "<<inFile.tellg()<<endl;
cout<<"共有记录数: "<<count<<",字节数: "<<nbyte<<endl;
inFile.clear();
inFile.seekg(0);
cout<<"位置指针: "<<inFile.tellg()<<endl;
inFile.read((char *)&stu,sizeof(stu));
cout<<left<<setw(10)<<stu.id<<" "<<setw(20)<<stu.name<<" "<<setw(3)<<right<<stu.score<<endl;
inFile.seekg(0,ios::end);
cout<<"位置指针: "<<inFile.tellg()<<endl;
inFile.close();
return 0;
}
打开文件时位置指针: 0
每个记录大小: 36
学生学号 姓名 成绩 流指针2019001001 zhangsan 90 36
2019001002 LiSi 100 72
2019001003 WangWu 78 108
2008001005 xuexiansheng 102 144
2011002004 qixiansheng 100 180
读取文件结束时位置指针: -1
共有记录数: 5,字节数: 180
位置指针: 0
2019001001 zhangsan 90
位置指针: 180
程序8-12 读取文件,查找最高分和最低分的学生,并显示信息。
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
class CStudent
{
public:
char id[11];
char name[21];
int score;
};
int main()
{
CStudent stu;
int count=0,maxScore,minScore,maxNo,minNo;
maxScore = INT_MIN;
minScore = INT_MAX;
maxNo = minNo = 0;
ifstream inFile("students.dat",ios::in|ios::binary);
if(!inFile)
{
cout<<"创建文件失败"<<endl;
return 0;
}
while(inFile.read((char *)&stu,sizeof(CStudent)))
{
if(stu.score > maxScore)
{
maxScore = stu.score;
maxNo = count;
}
else if(stu.score < minScore)
{
minScore = stu.score;
minNo = count;
}
count++;
}
inFile.clear();
inFile.seekg(sizeof(CStudent)*maxNo,ios::beg);
inFile.read((char *)&stu,sizeof(CStudent));
cout<<"最高分记录: "<<maxNo<<":"<<stu.id<<","<<stu.name<<","<<stu.score<<endl;
inFile.seekg(sizeof(CStudent)*minNo,ios::beg);
inFile.read((char *)&stu,sizeof(CStudent));
cout<<"最低分记录: "<<minNo<<":"<<stu.id<<","<<stu.name<<","<<stu.score<<endl;
inFile.close();
return 0;
}
最高分记录: 3:2008001005,xuexiansheng,102
最低分记录: 2:2019001003,WangWu,78
网友评论