【C++】第8章 文件操作

目录

8.1   文件的概念

8.2   C++ 文件流类

8.3   文件的打开和关闭

8.4   文件的读写

        8.4.1   文本文件的读写

        8.4.2   二进制文件的读写

8.5   文本方式打开文件与二进制方式打开文件的区别


8.1   文件的概念

        内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U盘等设备。为了便于数据的管理和检索,引人了 “文件” 的概念。一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。成千上万个文件如果不加分类放在一起,用户使用起来显然非常不便,因此又引人了树形目录(目录也叫文件夹)的机制,可以把文件放在不同的文件夹中,文件夹中还可以嵌套文件夹,这就便于用户对文件进行管理和使用,正如 Windows 的资源管理器呈现的那样。

        一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别,这是从文件的功能进行分类的。从数据存储的角度来说,所有的文件本质上都是一样的,都是由ー个个字节组成的,归根到底都是 0、1 比特串。不同的文件呈现出不同的形式(有的是文本,有的是视频等等),这主要是文件的创建者和解释者(使用文件的软件)约定好了文件格式。所谓“格式”,就是关于文件中每一部分的内容代表什么含义的一种约定。例如,常见的纯文本文件(也叫文本文件,扩展名通常是 “. txt” ),指的是能够在 Windows 的 “记事本” 程序中打开,并且能看出是一段有意义的文字的文件。文本文件的格式可以用—句话来描述:文件中的每个字节都是一个可见字符的 ASCII 码。

        除了纯文本文件外,图像、视频、可执行文件等一般被称作 “二进制文件”。二进制文件如果用 “记事本” 程序打开,看到的是一片乱码。所谓 “文本文件” 和 “二进制制文件” ,只是约定俗成的、从计算机用户角度出发进行的分类,并不是计算机科学的分类。因为从计算机科学的角度来看,所有的文件都是由二进制位组成的,都是二进制文件。文本文件和其他二进制文件只是格式不同而已。实际上,只要规定好格式,而且不怕浪费空间,用文本文件一样可以表示图像、声音、视频甚至可执行程序。简单地说,如果约定用字符 '1'、'2'、…、'7' 表示七个音符,那么由这些字符组成的文本文件就可以被遵从该约定的音乐软件演奏成一首曲子。

        下面再看一个用文本文件表示一幅图像的例子:一幅图像实际上就是一个由点构成的矩阵,每个点可以有不同的颜色,称为像素。有的图像是 256 色的,有的是 32 位真彩色(即一个像素的颜色用一个 32 位的整数表示)的。以 256 色图像为例,可以用 0~255 这 256 个数代表 256 种颜色,那么每个像素就可以用一个数来表示。再约定文件开始的两个数代表图像的宽度和高度(以像素为单位),则以下文本文件就可以表示一幅宽度为 6 像素、高度为 4 像素的 256 色图像:

                6 4

                24 0 38 129 4 154

                12 73 227 40 0 0

                12 173 127 20 0 0

                21 73 87 230 1 0

        这个 “文本图像” 文件的格式可以描述为:第一行的两个数分别代表水平方向的像素数目和垂直方向的像素数目,此后每行代表图像的一行像素,一行中的每个数对应于一个像素,表示其颜色。理解这一格式的图像处理软件就可以把上述文本文件呈现为一幅图像。视频是由每秒 24 幅图像组成的,因此用文本文件也可以表示视频。

        上面用文本文件表示图像的方法是非常低效的,浪费了太多的空间。文件中大量的空格是一种浪费。另外,常常要用 2 个甚至 3 个字符来表示一个像素,也造成大量浪费,因为用一个字节就足以表示 0 ~ 255 这 256 个数。因此,可以约定一个更节省空间的格式来表示一个256色的图像,此种文件格式的描述如下:文件中的第 0 和第 1 个字节是整数 n,代表图像的宽度( 2 字节的 n 的取值范围是 0~65 535,说明图像最多只能是 65 535 个像素宽),第 2 和第 3 个字节代表图像的高度。接下来,每 n 个字节表示图像的一行像素,其中每个字节对应于ー个像素的颜色。

        用这种格式存储 256 色图像,比用上面的文本格式存储图像能够大大节省空间。在 “记事本”程序中打开它,看到的就会是乱码,这个图像文件也就是所谓的 “二进制文件”。真正的图像文件、音频文件、视频文件的格式都比较复杂,有的还经过了压缩,但只要文件的制作软件和解读软件(如图像查看软件,音频、视频播放软件)遵循相同的格式约定,用户就可以在文件解读软件中看到文件的内容。

8.2   C++ 文件流类

        C++标准类库中有三个类可以用于文件操作,它们统称为文件流类。这三个类是:

        (1)ifstream:用于从文件中读取数据。

        (2)ofstream:用于向文件中写入数据。

        (3)fstream:既可以用于从文件中读取数据,又可以用于向文件中写入数据。

        使用这三个类时,程序中需要包含 fstream 头文件。C++类库中的流类如图 8.1 所示。

         ifstream 类和 fstream 类是从 istream 类派生而来的,因此 ifstream 类拥有 istream 类的全部成员函数。同样地,ostream 和 fstream 类也拥有 ostream 类的全部成员函数。这三个类中有一些十分熟悉的成员函数可以使用,如 operator << 、operator >> 、peek、ignore、getline、get等。

        在程序中,要使用一个文件,先要打开文件后才能读写,读写完后要关闭。创建一个新文件也要先执行打开(open)操作,然后才能往文件中写入数据。C++文件流类有相应的成员函数来实现打开、读、写、关闭等文件操作。

8.3   文件的打开和关闭

        在对文件进行读写操作之前,先要打开文件。打开文件有以下两个目的:

        (1)通过指定文件名,建立起文件和文件流对象的关联,以后要对文件进行操作时,就可以通过与之关联的流对象来进行。

        (2)指明文件的使用方式。使用方式有只读、只写、既读又写、在文件末尾添加数据、以文本方式使用、以二进制方式使用等多种。

        打开文件可以通过以下两种方式进行:

        (1)调用流对象的 open 成员函数打开文件。

        (2)定义文件流对象时,通过构造函数打开文件。

        关闭文件时,调用文件流对象的 close 成员函数即可。

        先看第一种文件打开方式。以 ifstream 类为例,该类有一个 open 成员函数,其他两个文件流类也有同样的 open 成员函数。

                void open(const char* szFileName, int mode)

        第一个参数是指向文件名的指针,第二个参数是文件的打开模式标记。文件的打开模式标记代表了文件的使用方式,这些标记可以单独使用,也可以组合使用。表 8.1 列出了各种模式标记单独使用时的作用,以及常见的两种模式标记组合的作用。

表8.1 文件打开模式标记
模 式 标 记适用对象作           用
ios::in

ifstream

fstream

        打开文件用于读取数据。如果文件不存在,则打开错误
ios::out

ofstream

fstream

        打开文件用于写入数据。如果文件不存在,则新建该文件;如果文件原来就存在,则打开时清除原来的内容
ios::app

ofstream

fstream

        打开文件,用于在其尾部添加数据。如果文件不存在,则新建该文件
ios::ateifstream        打开一个已有的文件,并将文件读指针指向文件末尾(读写指针的概念后面解释)。如果文件不存在,则打开出错
ios::truncofstream        单独使用时与 ios::out 相同
ios::binary

ifstream

ofstream

fstream

        以二进制方式打开文件。若不指定此模式,则以文本模式打开
ios::in | ios::outfstream        打开已存在的文件,既可以读取器内容,也可向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错
ios::in | ios::outofstream        打开已存在的文件,可以向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错
ios::in | ios::out | ios::truncfstream        打开已存在的文件,既可以读取器内容,也可向其写入数据。如果文件本来就存在,则打开时清除原来的内容;如果文件不存在,则新建该文件

        ios::binary 可以和其他模式标记组合使用,例如:

        ios::in | ios::binary 表示用二进制模式,以读取的方式打开文件。

        ios::out | ios::binary 表示用二进制模式,以写入的方式打开文件。

        文本方式与二进制方式打开文件的区别其实非常微小,后面会专门解释。一般来说,如过处理的是文本文件,那么用文本方式打开会方便一些,但其实任何文件都可以以二进制模式打开来读写。

        在流对象上执行 open 成员函数,给出文件名和打开模式,就可以打开文件。判断文件打开是否成功,可以看 “对象名” 这个表达式的值是否为 true,如果为 true,则表示文件打开成功。例如下面的程序:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	ifstream inFile;
	//具体路径根据个人而定
	inFile.open("c:\\tmp\\test.txt", ios::in);
	if(inFile)		//条件成立,则说明文件打开成功
		inFile.close();
	else
		cout << "test.txt doesn't exist" << endl;
	ofstream oFile;
	oFile.open("test1.txt", ios::out);
	if(!oFile)		//条件成立,则说明文件打开出错
		cout << "error 1" << endl;
	else
		oFile.close();
	oFile.open("tmp\\test2.txt", ios::out | ios::in);
	if(oFile)		//条件成立,则说明文件打开成功
		oFile.close();
	else
		cout << "error 2" << endl;
	fstream ioFile;
	ioFile.open("..\\test3.txt", ios::out | ios::in | ios::trunc);
	if(!ioFile)
		cout << "error 3" << endl;
	else
		ioFile.close();
	return 0;
}

        调用 open 成员函数时,给出的文件名可以是全路径的,如第 7 行的 “c:\\tmp\\test.txt”,指明文件在 c 盘的 tmp 文件夹中;也可以只给出文件名,如第 13 行的 “test1.txt”,这种情况下程序会在当前文件夹(也就是可执行程序所在的文件夹)中寻找要打开的文件。第 18 行的 “tmp\\test2.txt” 给出的是相对路径,说明 test2.txt 位于当前文件及的 tmp 子文件夹中。第 24 行的“..\\test3.txt” 也是相对路径,“..” 代表上一层文件夹,此时要到当前文件夹的上一层文件夹中查找 test3.txt。此外,“..\\..\\test4.txt”、“..\\tmp\\test4.txt” 等都是合法的带相对路径的文件名。

        定义流对象时,在构造函数中给出文件名和打开模式也可以打开文件。以 ifstream 类为例,它有如下构造函数:

                ifstream::ifstream(const char* szFileName, int mode = ios::in, int);

        第一个参数是指向文件名的指针;第二个参数是打开文件的模式标记,默认值为 ios::in;第三个参数是整型,也有默认值,一般极少使用。在定义流对象时打开文件的示例程序如下:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	ifstream inFile("c:\\tmp\\test.txt", ios::in);
	if(inFile)
		inFile.close();
	else
		cout << "test.txt doesn't exist" << endl;
	ofstream oFile("test1.txt", ios::out);
	if(!oFile)
		cout << "error 1";
	else
		oFile.close();
	fstream oFile2("tmp\\test2.txt", ios::out | ios::in);
	if(!oFile2)
		cout << "error 2";
	else
		oFile.close();
	return 0;
}

8.4   文件的读写

        8.4.1   文本文件的读写

        使用文件流对象打开文件后,文件就成为一个输入流或输出流。对于文本文件,可以使用 cin、cout 读写。前面讲述标准输入输出流时提到,流的成员函数和流操纵算子同样适用于文件流,因为 ifstream 是 istream 的派生类,ofstream 是 ostream 的派生类,fstream 是 iostream 的派生类,而 iostream 又是从 istream 和 ostream 共同派生而来的。

        例题:编写一个程序,将文件 in.txt 中的整数排序后输出到 out.txt。例如,若 in.txt 内容为:

                1 234 9 45

                6 879

则执行本程序后,生成的 out.txt 的内容为:

                1 6 9 45 234 879

        假设 in.txt 中的整数不超过 1000 个。

        示例程序如下:

#include <iostream>
#include <fstream>
#include <cstdlib> //qsort在此头文件中声明
using namespace std;
const int MAX_NUM = 1000;
int a[MAX_NUM]; //存放文件中读入的整数
int MyCompare(const void * e1, const void * e2)
{ //用于qsort的比较函数
 	return *((int *)e1) - *((int *)e2);
}
int main()
{
	int total = 0;//读入的整数个数
	ifstream srcFile("in.txt",ios::in); //以文本模式打开in.txt备读
	if(!srcFile) { //打开失败
		cout << "error opening source file." << endl;
		return 0;
	}
	ofstream destFile("out.txt",ios::out); //以文本模式打开out.txt备写
	if(!destFile) {
		srcFile.close(); //程序结束前不能忘记关闭以前打开过的文件
		cout << "error opening destination file." << endl;
		return 0;
	}
	int x;	
	while(srcFile >> x) //可以像用cin那样用ifstream对象
		a[total++] = x;
	qsort(a,total,sizeof(int),MyCompare); //排序
	for(int i = 0;i < total; ++i)
		destFile << a[i] << " "; //可以像用cout那样用ofstream对象
	destFile.close();
	srcFile.close();
	return 0;
}

        程序中如果用二进制方式打开文件,结果毫无区别。

        第 21 行是初学者容易忽略的。程序结束前不要忘记关闭以前打开过的文件。

        8.4.2   二进制文件的读写

        1. 为什么要用二进制文件

        用文本方式储存信息不但浪费空间,而且不便于检索。例如,一个学籍管理程序需要记录所有学生的学号、姓名、年龄信息,并且能够按照姓名查找学生的信息。程序中可以用一个类来表示学生:

                class CStudent{

                        char szName[20];        //假设学生姓名不超过 19 个字符,以 ‘\0’ 结尾

                        char szId[10];              //假设学号为 9 位,以 ‘\0’ 结尾

                        int age;

                }

        如果用文本文件储存学生的信息,文件可能是如下样子:

                Micheal Jackson 110923412   17

                Tom Hanks 110923413   18

                ...

        这种存储方式不但浪费空间,而且查找效率低下。因为每个学生的信息所占用的字节数不同个,所以即使文件中的学生信息是按姓名排好序的,要用程序根据名字进行查找仍然没有什么好办法,只能在文件中从头到尾搜索。如果把全部学生信息都读入内存并排序后再查找,当然速度会很快,但如果学生数巨大,则把所有学生信息都读入内存可能是不现实的。

        可以用二进制的方式来存储学生信息,即把 CStudent 对象直接写入文件。在该文件中,每个学生的信息都占用 sizeof(CStudent) 个字节。对象写入文件后一般称作 “记录”。本例中,每个学生都对应于一条记录。该学生记录文件可以按姓名排序,则使用折半查找的效率会很高。

        读写二进制文件不能使用前面提到的类似于 cin、cout 从流中读写数据的方法。这时可以调用 ifstream 对象和 fstrea 对象的 read 成员函数从文件中读取数据,调用 ofstr 和 fstream 的 write 成员函数向文件中写入数据。

        2. 用 ostream::write 成员函数写文件

        ofstream 和 fstream 的 write 成员函数实际上继承自 ostream 类,原型如下:

                ofstream & write(char* buffer, int count);

        该成员函数将内存中buffer 所指向的 count 个字节的内容写入文件,返回值是对函数所作用的对象的引用,如 obj.write(...) 的返回值就是对 obj 的引用。

        write 成员函数向文件写入若干字节,可是调用 write 函数时并没有指定这若干字节要写入文件的什么位置。那么,write 函数在执行过程中到底把这若干字节写到哪里呢?答案是从文件写指针指向的位置开始写入。

        文件写指针是 ofstream 或 fstream 对象内部维护的一个变量。文件刚刚打开时,文件写指针指向文件的开头(如果以 ios::app 方式打开,则指向文件末尾),用write 函数写入 n 个字节,写指针指向的位置就向后移动 n 个字节。

        下面的程序从键盘输入几名学生的姓名和年龄(输入时,在单独的一行中按 Ctrl+Z 键再按回车键以结束输入。假设学生姓名中都没有空格),并以二进制文件形式储存,成为一个学生记录文件 students.dat。

#include <iostream>
#include <fstream>
using namespace std;
class CStudent{
	public:
		char szName[20];
		int age;
};
int main()
{
	CStudent s;
	ofstream outFile("student.dat", ios::out | ios::binary);
	while(cin >> s.szName >> s.age)
		outFile.write((char*)&s, sizeof(s));
	outFile.close();
	return 0;
}

        输入:

                Tom 60(回车)

                Jack 80(回车)

                Jane 40(回车)

                ^Z(回车)

则形成的 student.dat 为 72 字节,用 “记事本” 程序打开呈现乱码:

        Tom烫烫烫烫烫烫烫烫        Jack烫烫烫烫烫烫烫?        Jane烫烫烫烫烫烫烫?

        第13行指定文件的打开模式是 ios::out | ios::binary,即以二进制写模式打开。在 Windows 平台中,用二进制模式打开是必要的,否则可能出错,原因会在 8.5 节 :“文本方式打开文件与二进制方式打开文件的区别” 介绍。

        第15行将 s 对象写入文件。s 的地址就是要写入文件的内存缓冲区的地址。但是 &s不是 char* 类型,因此要进行强制类型转换。

        第16行,文件使用完毕一定要关闭,否则程序结束后文件的内容可能不完整。

        3. 用 istream::read 成员函数读文件

        ifstream 和 fstream 的 read 成员函数实际上继承自 istream 类,原型如下:

                istream & read(char* buffer, int count);

        该成员函数从文件中读取 count 个字节的内容,存放到 buffer 所指向的内存缓冲区,返回值是对函数所作用的对象的引用。如果想知道一共成功读取了多少个字节(读到文件尾时,未必能读取 count 个字节),可以在 read 函数执行后立即调用文件流对象的 gcount 成员函数,其返回值就是最近一次 read 函数执行时成功读取的字节数。gcount 是 istream 类的成员函数,原型如下:

                int gcount();

        read 成员函数从文件读指针指向的位置开始读取若干字节。文件读指针是 ifstream 或 fstream 对象内部维护的一个变量。文件刚打开时,文件读指针指向文件的开头(如果以 ios::ate 方式打开,则指向文件末尾),用 read 函数读取 n 个字节,读指针指向的位置就向后移动 n 个字节。因此,打开一个文件后连续调用 read 函数,就能将整个文件的内容读取出来。

        下面的程序将前面创建的学生记录文件 students.dat 的内容读出来并显示。

#include <iostream>
#include <fstream>
using namespace std;
class CStudent
{
	public:
		char szName[20];
		int age;
};
int main()
{
	CStudent s;		
	ifstream inFile("students.dat",ios::in|ios::binary); //二进制读方式打开
	if(!inFile) {
		cout << "error" <<endl;
		return 0;
	}
	while(inFile.read((char *)&s, sizeof(s))) { //一直读到文件结束
		int readedBytes = inFile.gcount(); //看刚才读了多少字节
		cout << s.szName << " " << s.age << endl;	
	}
	inFile.close();
	return 0;
}

        程序的输出结果是:

                Tom 60

                Jack 80

                Jane 40

        第 18 行,判断文件是否已经读完的方法和 while(cin >> n) 类似,归根到底都是因为 istream 类重载了 bool 强制类型转换运算符。

        第 19 行,只是演示 gcount 函数的用法,删除该行对程序运行结果没有影响。

        思考题:关于 students.dat 的两个程序中,如果 CStudent 类的 szName 的定义不是 “char szName[20]” 而是 “ string szName”,是否可以?为什么?

        答:不行,read 成员函数本质上是把指针里的内容读出来,而 string 类变量本身的指针内容是指向存储字符串的一片动态分配的内存,故读出来是地址而非字符串。

        4. 用文件流类的 put 和 get 成员函数读写文件

        可以用 ifstream 和 fstream 类的 get 成员函数(继承自 istream 类)从文件中一次读取一个字节,也可以用 ofstream 和 fstream 类的 put 成员函数(继承自 ostream 类)向文件中一次写入一个字节。

        例如:编写一个 mycopy 程序,实现文件复制的功能。用法是在 “命令提示符” 窗口输入:                       mycopy 源文件名 目标文件名

就能将源文件复制到目标文件。例如:

                mycopy src.dat dest.dat        

即将 src.dat 复制到 dest.dat。如果 dest.dat原本就存在,则原来的文件会被覆盖。

        解题的基本思路是每次从源文件读取一个字节,然后写入目标文件。程序如下:

#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
	if(argc != 3){
		cout << "File name missing!" << 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;
}

        文件存放于磁盘中,磁盘的访问速度远远低于内存。如果每次读一个字节或写一个字节都要访问磁盘,那么文件的读写速度就会慢的不可忍受。因此,操作系统在接收到读文件的请求时,哪怕只要读一个字节,也会把一片数据(通常至少是 512 个字节,因为磁盘的一个扇区是 512 B)都读到一个操作系统自行管理的内存缓冲区中,当要读下一个字节时,就不需要访问磁盘,直接从该缓存区中读取就可以了。操作系统在接收到写文件的请求时,也是先要把写入的数据在一个内存缓冲区中保存起来,等缓冲区满后,再将缓冲区的内容全部写入磁盘。关闭文件的操作就能确保内存缓冲区中的数据被写入磁盘。

        尽管如此,要连续读写文件时,像 mycopy 程序那样一个字节一个字节地读写,还是不如一次读写一片内存域块。每次读写的字节数最好是 512 的整数倍。

        8.4.3   操作文件度写指针

        在读写文件时,有时希望直接跳到文件中的某处开始读写忙着就需要先将文件的读写指针指向该处,然后再进行读写。ifstream 类和 fstream 类有 seekg 成员函数,可以设置文件读指针的位置;ofstream 类和 fstream 类有 seekp 成员函数,可以设置文件写指针的位置。所谓 “位置”,就是距离文件开头有多少个字节。文件开头的位置是 0.这两个函数的原型如下:

                ostream & seekp(int offset, int mode);

                istream  & seekg(int offset, int mode);

        mode 代表文件读写指针的设置模式,有以下三种选项:

        ios::beg:让文件读指针(或写指针)指向从文件开始向后的 offset 字节处。offset 等于 0 即代表文件开头。在此情况下,offset 只能是非负数。

        ios::cur:在此情况下,offset 为负数则表示将读指针(或写指针)从当前位置朝文件开头方向移动 offset 个字节,为正数则表示将读指针(或写指针)从当前位置朝文件尾部移动 offset 字节,为 0 则不移动。

        ios::end :让文件读指针(或写指针)指向从文件结尾往前的 |offset|(offset 的绝对值)字节处。在此情况下,offset 只能是 0 或者负数。

        ifstream 类和 fstream 类还有 tellg 成员函数,能够返回文件读指针的位置;ofstream 类和 fstream 类还有 tellp 成员函数,能够返回文件写指针的位置。这两个成员函数的原型如下:

                int tellg();

                int tellp();

        要获取文件长度,可以用 seekg 函数将文件读指针定位到文件尾部,再用 tellg 函数获取文件读指针的位置,此位置即为文件长度。

        例题:假设学生记录文件 students.dat 是按照姓名排好序的,编写程序没在 students.dat 文件中用折半查找的方法找到姓名为 Jack 的学生记录,并将其年龄改为 20(假设文件很大,无法全部读入内存)。程序如下:

#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
class CStudent
{
	public:
		char szName[20];
		int age;
};
int main()
{
	CStudent s;		
	fstream ioFile("students.dat", ios::in|ios::out);//用既读又写的方式打开
	if(!ioFile) {
		cout << "error" ;
		return 0;
	}
	ioFile.seekg(0,ios::end); //定位读指针到文件尾部,
                              //以便用以后tellg 获取文件长度
	int L = 0,R; // L是折半查找范围内第一个记录的序号
                  // R是折半查找范围内最后一个记录的序号
	R = ioFile.tellg() / sizeof(CStudent) - 1;
    //首次查找范围的最后一个记录的序号就是: 记录总数- 1
	do {
		int mid = (L + R)/2; //要用查找范围正中的记录和待查找的名字比对
		ioFile.seekg(mid *sizeof(CStudent),ios::beg); //定位到正中的记录
		ioFile.read((char *)&s, sizeof(s));
		int tmp = strcmp( s.szName,"Jack");
		if(tmp == 0) { //找到了
			s.age = 20;
			ioFile.seekp(mid*sizeof(CStudent),ios::beg);
			ioFile.write((char*)&s, sizeof(s));
			break;
		}
		else if (tmp > 0) //继续到前一半查找
			R = mid - 1 ;
		else  //继续到后一半查找
			L = mid + 1;
	}while(L <= R);
	ioFile.close();
	return 0;
}

8.5   文本方式打开文件与二进制方式打开文件的区别

        在 UNIX/Linux 平台中,用文本方式或二进制方式打开文件没有任何区别。

        在 UNIX/Linux 平台中,文本文件以 ‘\n’ (ASCII 码为 0x0a)作为换行符号;而在 Windows 平台中,文本文件以连在一起的 “\r\n” (‘\r’ 的 ASCII 码是 0x0d)作为换行符号。在 Windows 平台中,如果以文本方式打开文件,当读取文件时,系统会将文件中所有的 “\r\n” 转换成一个字符 ‘\n’。即,如果文件中共有连续的两个字节是 0x0d0a,则系统会丢弃前面的 0x0d 这个字节,制只读入 0x0a;当写入文件时,系统会将 ‘\n’ 转换成 “\r\n” 写入。即,如果要写入的内容中有字节为 0x0a,则在写入该字节前,系统会自动先写入一个 0x0d。因此,如果用文本方式打开二进制文件进行读写,读写的内容就看和文件的内容公有出入。因此,用二进制方式打开文件总是最保险的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第8章主要介绍了C++中的文件操作,包括文件的打开、读写、关闭等基本操作。下面是一些重点内容: 1. 文件的打开 可以使用fstream库中的ifstream、ofstream、fstream三个类分别表示输入文件流、输出文件流、输入输出文件流。它们的构造函数可以传入文件名和打开模式,例如: ```c++ #include <fstream> using namespace std; int main() { // 打开一个文本文件用于输入 ifstream input("input.txt"); if (!input) { cout << "打开文件失败!" << endl; return 1; } // 打开一个文本文件用于输出 ofstream output("output.txt"); if (!output) { cout << "打开文件失败!" << endl; return 1; } // 打开一个文本文件用于输入输出 fstream io("data.txt", ios::in | ios::out); if (!io) { cout << "打开文件失败!" << endl; return 1; } // ... } ``` 2. 文件的读写 使用文件流对象的<<、>>、getline等操作符进行读写。例如: ```c++ #include <fstream> #include <iostream> using namespace std; int main() { // 打开一个文本文件用于输入 ifstream input("input.txt"); if (!input) { cout << "打开文件失败!" << endl; return 1; } // 打开一个文本文件用于输出 ofstream output("output.txt"); if (!output) { cout << "打开文件失败!" << endl; return 1; } int x; input >> x; // 从文件中读取一个整数 output << "x = " << x << endl; // 将x输出到文件中 string line; getline(input, line); // 从文件中读取一行字符串 output << line << endl; // 将字符串输出到文件中 // ... } ``` 3. 文件的关闭 使用文件流对象的close()方法来关闭文件。例如: ```c++ #include <fstream> using namespace std; int main() { // 打开一个文本文件用于输入 ifstream input("input.txt"); if (!input) { cout << "打开文件失败!" << endl; return 1; } // 关闭文件 input.close(); } ``` 以上是新标准C++程序设计第8章文件操作的一些重点内容,希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值