背景
如果将数据都写入到内存中,那么程序停止运行后,内存就会被全部回收,数据也随之消失。这并不是我们想要的。
将数据写入到硬盘中,就可以永久保存,下次运行程序之后读取即可。
专业术语
- 文件的输入
即读取文件内容。
- 文件的输出
即往文件里面写入数据。
关于C++文件操作
- 这里我们利用头文件fstream来对文件进行操作。所以需要引入头文件:
#include<fstream>
- 头文件fstream中定义了很多类,包括ifstream、ofstream和fstream等类。通过这些类,就可以对文件进行写入和读取了。
- ifstream用于读取文件
- ofstream用于写入文件
- fstream既可以用于读取,也可以用于写入。
普通文件和二进制文件
C++有两种文件类型,普通文件和二进制文件。
普通文件
普通文件类似于txt文本文件,存放的内容为字符。
普通文件为明文存取,用户只要得到这个文件,就能看到里面的内容。
因为普通文件时明文存取,所以不适合存放密码等信息。
二进制文件
二进制文件在存储空间中由0和1组成,只有用户知道怎么去分析这些0和1,进而知道此文件的真实信息。
所以二进制文件更为适合存放密码等信息(相对来说)。
如果强制打开此二进制文件,就会出现乱码。(因为打开此文件的软件也不知道如何解析)
- 在C++中,常常用于存取对象
普通文件的读写
普通文件的输出(写入)
ofstream outfile; //输出文件流
outfile.open("test.txt", ios::out );
if (! outfile.is_open()) //打开失败则退出
{ cout << "Error opening file"; exit (1); }
outfile.write("123456",6); //写入6字节
outfile.close(); //关闭文件
解释:
- 创建对象,通过此对象对文件进行操作。
- 打开文件,指定打文件的路径和打开方式(输出或输入)。
- 判断打开文件是否成功,打开失败则退出程序。
- 写入文件,给出写入的内容和写入的字节数。
- 关闭文件。
其中:
- ios::out是操作属性,out表示文件输出。
- 如果文件存在,则清空文件。
- 如果文件不存在,则创建文件。
- ofstream中的o表示output输出,它实例化的对象默认为ios::out,所以这里可以省略第二个参数ios::out。
- 有很多原因会造成文件打开失败:
- 其他程序占用此文件(被其他程序打开了此文件),所以我们最后一定不要忘记关闭这个文件。
- 磁盘损坏。
- 文件损坏。
- 没有权限。(管理员没有授予当前用户权限)
- 成员函数is_open()用于判断文件是否打开成功,成功返回true。
outfile.write("123456",6)
表示写入的内容为"123456",写入6个字节。
普通文件的输入(读取)
文件的输入和前面的例子套路类似
ifstream infile;
infile.open("test.txt", ios::in );
if (! infile.is_open()) //打开失败则退出
{ cout << "文件打开失败"; exit (1); }
char buffer;
while (infile.get(buffer)){ //如果读文件到达文件末尾,返回false。
cout << buffer;
}
infile.close(); //关闭文件
解释:
- 创建对象,通过此对象对文件进行操作。
- 打开文件,指定打文件的路径和打开方式(输出或输入)。
- 判断打开文件是否成功,打开失败则退出程序。
- 读取文件。
- 关闭文件。
关于读取文件:
infile.get(buffer)
表示读取一个字符,保存到变量buffer中。如果读取成功,则返回true,否则返回false。
二进制文件的读写
C++常将对象写入二进制文件中
类中通常会有不同类型的变量,如
Student
中的char name[10]
和int age
。
如果将很多学生都写入文件,将name和age分开。那么读取的时候就会变得非常麻烦,因为他们占用的字节数不一样,而且每个人的名字的字数也不一样。
如果使用二进制,就会变得非常方便。
二进制文件的输出(写入)
#include<iostream>
#include<fstream>
using namespace std;
class Student{
public:
char name[10];
int age;
char classroom[10];
Student(char *name,int age,char classroom[10]){
strcpy_s(this->name, name);
this->age=age;
strcpy_s(this->classroom,classroom);
}
};
int main(){
Student s1("张三",18,"1班");
Student s2("李四",20,"2班");
//二进制输出
ofstream outfile; //输出文件流
outfile.open("test.bin", ios::out | ios::binary );
if (! outfile.is_open()) //打开失败则退出
{ cout << "Error opening file"; exit (1); }
outfile.write((char*)&s1, sizeof(s1)); //写入对象s1
outfile.write((char*)&s2, sizeof(s2)); //写入对象s2
outfile.close(); //关闭文件
}
解释:
- 通常用后缀.bin来表示二进制文件,打开属性为ios::binary。
- 不同的属性组合用或运算
ios::out | ios::binary
表示。 - 写入对象时,需要将对象转化为基本数据类型,这里将s1和s2转化为了char类型。
- 写入对象时,同样需要指定写入大小,我们可以借用sizeof来自动判断对象的大小。
- 这个例子依次写入了s1和s2。
二进制文件的输入(读取)
#include<iostream>
#include<fstream>
using namespace std;
class Student{
public:
char name[10];
int age;
char classroom[10];
Student(char *name,int age,char classroom[10]){
strcpy_s(this->name, name);
this->age=age;
strcpy_s(this->classroom,classroom);
}
};
int main(){
Student s("",0,"0班");
//二进制输入
ifstream infile;
infile.open("test.bin", ios::binary );
if (! infile.is_open()) //打开失败则退出
{ cout << "Error opening file"; exit (1); }
while(infile.read((char *)&s, sizeof(s))) { //一直读到文件结束
int readedBytes = infile.gcount(); //看刚才读了多少字节
cout<<"读取"<<readedBytes<<"字节"<<endl;
cout << s.name << " " << s.age << " " << s.classroom<< endl;
}
}
结果:
张三 18 1班
李四 20 2班
解释:
- 变量s用于保存读取的内容
(infile.read((char *)&s, sizeof(s))
表示读取数据到变量s中,指定读取的大小,函数会以char类型读取,自动转化为Student的数据成员各自的类型。infile.gcount()
可以得到上一次读取的字节数。- 通过while循环,依次读取所有的对象。(如上例中的s1和s2)
读取模式
前面的例子中:ios::out表示的就是读取模式。
ios::out表示输出。
常用的模式:
- ios::in //打开文件用于读取
- ios::out //打开文件用于写入
- ios::app //追加模式。所有写入都追加到文件末尾
- ios::ate //文件打开后将指针定位到文件末尾(常和fstream结合)
- ios::trunc //文件存在则清空
- ios::binary //二进制方式读写
小贴士:
ifstream创建的对象默认就是ios::in。
ofstream创建的对象默认就是ios::out。
fstream需要手动指定输入或输出。
多种模式一起使用:
ios::in | ios::binary
举例:
使用fstream、二进制、输出、追加的方式打开文件:
fstream file;
file.open("test.bin", ios::binary | ios:out | ios::ate );
成员函数
fstream file; //定义对象
file.open("test.bin", ios:out | ios:in );
file.is_open() //是否打开成功
file.eof() //读指针是否到达结尾
file.read (buffer, 6); //读出6字节
file.write("123456",6); //写入6字节
文件指针
C++中读取文件是通过指针来读取的。
文件的位置决定下一个读取的字符、下一个写入字符的位置。
获取指针的位置:
tellg() //得到当前读指针位置
tellp() //得到当前写指针位置
修改指针的位置:
seekg(-4,ios::cur) //读指针当前位置前移4个字节
seekp(-4,ios::cur) //写指针当前位置前移4个字节
其中:
ios::cur表示当前指针位置
ios::beg表示从流开始位置计算的位移(即文件开头)
ios::end表示从流末尾处开始计算的位移(即文件结尾)