(十一)C++学习 | 文件读写


1. 简介

下面是在 C + + {\rm C++} C中有关文件读写的几个类ifstreamofstreamfstream的继承和派生关系。本文将介绍在 C + + {\rm C++} C中有关文件读写的操作。
在这里插入图片描述


2. 文件基本操作

2.1 创建文件

首先包含头文件#include<fstream>,然后使用以下基本语句创建文件

ofstream outFile("clients.dat", ios::out | ios::binary);

上述语句是调用ofstream类的构造函数来创建文件,其中几个参数的含义如下:

  • clients.dat:待创建的文件的名称;
  • ios::out:创建文件的打开方式,这里是ios::out表示如果要创立的文件已经存在,则新建文件将会覆盖旧文件;ios::app表示如果要创建的文件已经存在,则新建文件的内容将会追加到旧文件上;
  • ios::binary:以二进制文件格式打开文件。

在第二部分参数中,借助或运算来同时设置多个参数值。此外,我们也可以先创建类对象,然后调用成员函数创建文件:

ofstream fout;
fout.open("test.out", ios::out | ios::binary);

然后使用以下语句判断文件是否创建成功:

if (!fout) {
	cout << "File open error!" << endl;
}

其中,文件名参数可以指定为绝对路径或相对路径。如果没有指定具体路径信息,则程序在当前文件夹下寻找。

2.2 文件的读写指针

在文件的读写中,我们以一个读写指针来判断我们当前操作的具体位置。这与普通意义的指针类型不同,这是文件操作中所特有的一种类型。也就是说,文件的读写指针用于标识文件操作的当前位置,该指针的位置即为读写操作的位置

ofstream fout("a1.out", ios::app);	// 在当前目录下创建文件a1.out
long location = fout.tellp();		// 取得写指针的位置
location = 10;			
fout.seekp(location);				// 将写指针移至第10个字节处
fout.seekp(location, ios::beg);		// 将写指针移至从头开始第10个字节处
fout.seekp(location, ios::cur);		// 将写指针移至从当前位置开始第10个字节处
fout.seekp(location, ios::end);		// 将写指针移至从尾开始第10个字节处

其中,上面location的值可以为负值。上面例子是文件写指针的例子,文件读指针的使用方法类似。

ofstream fin("a1.out", ios::app);	// 在当前目录下创建文件a1.out
long location = fin.tellg();		// 取得读指针的位置
location = 10L;			
fin.seekg(location);				// 将读指针移至第10个字节处
fin.seekg(location, ios::beg);		// 将读指针移至从头开始第10个字节处
fin.seekg(location, ios::cur);		// 将读指针移至从当前位置开始第10个字节处
fin.seekg(location, ios::end);		// 将读指针移至从尾开始第10个字节处

2.3 字符文件的读写

字符文件就是我们常使用的记事本文件,通常其后缀名是.txt。首先,由于文件流也是流,所以前面介绍的流的成员函数和流操作算子也同样适用于文件流。下面是一段程序,其完成的功能是将文件in.txt内的整数排序后,再将排序后的结果写入out.txt中。例如,in.txt文件的内容是1 234 9 45 6 879;则执行程序后,生成的out.txt文件的内容是1 6 9 45 234 879

#include<iostream>
#include<fstream>
#include<vector>
#include<algorithm>

using namespace std;

int main() {
	vector<int> nums;
	// 创建源文件和目标文件
	ifstream srcFile("in.txt", ios::in);
	ofstream dstFile("out.txt", ios::out);
	// 从源文件内读取数据
	int x;
	while (srcFile >> x)
	{
		nums.push_back(x);
	}
	// 对数组排序
	sort(nums.begin(), nums.end());
	// 将排序后的数组写入目标文件内
	for (int i = 0; i < nums.size(); ++i) {
		dstFile << nums[i] << " ";
	}
	// 关闭文件
	srcFile.close();
	dstFile.close();
	return 0;
}

2.4 二进制文件的读写

2.4.1 二进制读文件

首先来看在二进制读文件中常用到的函数:

istream& read(char* s, long n);

函数的作用是将文件读指针指向的地方往后的n个字节的内容读入到内存地址s处,同时文件读指针也会相应地移动n个字节(以ios::in的方式打开文件时,文件的读指针位于文件开头,这时我们可以不断调用read不断读取整个文件的内容)。

2.4.2 二进制写文件

与二进制读文件类似,二进制写文件中常用到的函数是:

istream& write(const char* s, long n);

函数的作用是将内存地址s处的n个字节的内容写入到文件中写指针的位置,同时同时文件写指针也会相应地移动n个字节(以ios::out的方式打开文件时,文件的写指针位于文件开头;以ios::app的方式打开文件时,文件的写指针位于文件结尾)。

2.4.3 例子:二进制文件写入和读取一个整数

#include<fstream>
#include<iostream>

using namespace std;

int main() {
	// 创建二进制文件some.dat
	ofstream fout("some.dat", ios::out | ios::binary);
	// 写入整数
	int x = 100;
	fout.write((const char*)(&x), sizeof(int));
	fout.close();
	// 打开二进制文件some.dat
	ifstream fin("some.dat", ios::in | ios::binary);
	// 读取整数
	int y;
	fin.read((char*)(&y), sizeof(int));
	fin.close();
	// 输出
	cout << y << endl;
	return 0;
}

2.4.4 例子:从键盘输入几个学生的姓名和成绩,并以二进制形式保存

#include<fstream>
#include<iostream>

using namespace std;

// 结构体,学生包含姓名和成绩信息
struct Student {
	char name[20];
	int score;
};
int main() {
	Student s;
	ofstream outFile("students.dat", ios::out | ios::binary);
	// 从键盘读入学生信息并保存至二进制文件中
	while (cin >> s.name >> s.score)
	{
		outFile.write((char*)(&s), sizeof(s));
	}
	outFile.close();
	return 0;
}

运行上述程序,在当前工作目录下生成文件students.dat,其大小为72字节。如果用记事本打开该文件则会出现乱码,现在我们用程序来查看该二进制文件中的内容:

#include<fstream>
#include<iostream>

using namespace std;

// 结构体,学生包含姓名和成绩信息
struct Student {
	char name[20];
	int score;
};
int main() {
	Student s;
	// 打开该二进制文件
	ifstream inFile("students.dat", ios::in | ios::binary);
	// 如果文件不存在
	if (!inFile) {
		cout << "error!" << endl;
		return 0;
	}
	// 从二进制文件中读取并打印
	while (inFile.read((char*)(&s), sizeof(s)))
	{
		cout << s.name << " " << s.score << endl;
	}
	inFile.close();
	return 0;
}

运行上述程序,屏幕显示出刚才我们从键盘输入的内容。

2.4.5 例子:修改二进制文件中的内容

假如上述例子中,从键盘读入的内容是小明 2\n 小张 8\n 小红 5\n,下面程序的功能是将小红更改为小李

#include<fstream>
#include<iostream>

using namespace std;

// 结构体,学生包含姓名和成绩信息
struct Student {
	char name[20];
	int score;
};
int main() {
	Student s;
	// fstream类对象既可以对文件读,也可以对文件写
	fstream iofile("students.dat", ios::in | ios::out | ios::binary);
	if (!iofile) {
		cout << "error!" << endl;
		return 0;
	}
	// 将写指针定位到第三条记录,即将写指针向后移动2条记录的位置
	iofile.seekp(2 * sizeof(s), ios::beg);
	// 写入修改后的内容
	iofile.write("小李", strlen("小李") + 1);
	// 将读指针定位到文件开头
	iofile.seekg(0, ios::beg);
	// 读取二进制文件内容并打印
	while (iofile.read((char*)(&s), sizeof(s))) {
		cout << s.name << " " << s.score << endl;
	}
	iofile.close();
	return 0;
}

2.4.6 例子:文件拷贝

编写程序完成以下功能copy src.dat dst.dat,即将src.dat的内容拷贝到dst.dat中。如果原dst.dat已经存在,则原来的文件将会被覆盖。

#include<fstream>
#include<iostream>

int main(int argc, char* argv[]) {
	// 程序接受两个参数copy src.dat和dst.dat
	if (argc != 3) {
		cout << "File name missing!" << endl;
		return 0;
	}
	// 打开文件并判断是否成功
	ifstream inFile(argv[1], ios::in | ios::binary);
	if (!inFile) {
		cout << "Source file open error!" << endl;
		return 0;
	}
	// 打开文件用于写操作
	ofstream outFile(argv[2], ios::out | ios::binary);
	if (!outFile) {
		cout << "New file open error!" << endl;
		inFile.close();
		return 0;
	}
	// 拷贝
	char c;
	while (inFile.get(c)) {
		outFile.put(c);
	}
	inFile.close();
	outFile.close();
	return 0;
}

3. 二进制文件和文本文件的区别

在不同的操作系统中,二进制文件和文本文件的主要区别是换行符号。在 L i n u x / U n i x {\rm Linux/Unix} Linux/Unix系统中,换行符用\n表示, A S C I I {\rm ASCII} ASCII码值是0x0a;在 W i n d o w s {\rm Windows} Windows系统中,换行符用\r\n表示, A S C I I {\rm ASCII} ASCII码值是0x0d0a M a c   O S {\rm Mac\ OS} Mac OS系统中,换行符用\r表示, A S C I I {\rm ASCII} ASCII码值是0x0d。所以,有时候我们在不同系统之间打开记事本文件时,文本没有换行。

同时,在 L i n u x / U n i x {\rm Linux/Unix} Linux/Unix系统下,打开二进制文件时是否使用ios::binary没有显著的区别;而在 W i n d o w s {\rm Windows} Windows系统下,如果不使用ios:;binary,则读取文件时,所有的\r\n只会被当作一个字符\n处理,即少读了一个\r;在写入文件时,单独写入\n时,系统会在其前自动加上一个\r,即多写入了一个\r


4. 总结

本文介绍在 C + + {\rm C++} C中的读写文件操作,主要介绍了二进制文件的读写操作。相比于文本文件,二进制文件的存储更加节省空间。不管是什么语言,在文件读写操作时,很重要的一点时相关操作完成后必须关闭文件,避免造成不可知的错误。


参考

  1. 北京大学公开课:程序设计与算法(三)C++面向对象程序设计.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值