C++流和文件IO

写在前面:本文章来自于在学习过程中的总结,供大家参考。因水平有限,博客中难免会有不足,恳请大佬们不吝赐教!

相关文章:
[C++系列]小白的C++入门教程
C++流IO工具
C++字符IO


一、流和基本文件I/O

是由字符(或其他类型的数据)构成的“流”。流向程序,称为输入流。流出程序,称为输出流。如cin是连接键盘的输入流,cout是连接屏幕的输出流。

1.文件I/O

C++的文件操作就是指对文件进行读写的操作,那么 IO 呢?字母 I 就是 Input 的缩写,意为输入,字母 O 就是 Output 的缩写,意为输出。所以文件的 IO 操作就是指文件的输入与输出。输入就是从磁盘上的文件中读取内容到内存中输出就是将内存中的数据内容输出或者说写入到磁盘的文件中。(也就是说,输入和输出是相对于内存中的数据而言的

fstream头文件定义了三种支持文件IO的类型:

  1. ifstream,由istream派生而来,提供读文件的功能。
  2. ofstream,由ostream派生而来,提供写文件的功能。
  3. fstream,由iostream派生而来,提供和读写同一个文件的功能。

在C++中,流是一种称为“对象”的特殊变量

2.文件的类型

文件的类型分为文本文件和二进制文件,文本文件又称为ASCII文件,它的每个字节存放一个ASCII码,代表一个字符。二进制文件则是把内存中的数据,按照其在内存中的存储形式原样写在磁盘上存放。比如一个 short 类型的整数20000,在内存中占用2个字节,而按文本形式输出则占5个字节。因此在以文本形式输出时,一个字节对应一个字符,因而便于字符的输出,缺点则是占用存储空间较多。用二进制形式输出数据,节省了转化时间和存储空间,但不能直接以字符的形式输出。所以,大家可以根据自己的需要选择使用文本文件还是二进制文件存储。如果是输出log文件之类的,那肯定就得用文本形式了,二进制的也看不懂啊,对不对?

二、文件的读写

欲对文件进行读写操作,首先含入fstream头文件:

#include <fstream>
数据类型描述
ofstream该数据类型表示输出文件流,用于创建文件并向文件写入信息
ifstream该数据类型表示输出文件流,用于从文件读取信息
ftream该数据类型通常表示文件流,同时具有ofstream和ifstream两种功能,可以创建文件,向文件写入信息,从文件读取信息

1.打开文件

为了开启一个可供输入的文件,我们定义一个ofstream(供输出用的file stream)对象,并将文件名传入
open()函数是fstream、ifstream和ofstream队形的一个成员,下面是open()函数的标准语法:

void open(const char *filename,ios::openmode mode);

open()成员函数的第一参数指定打开的文件的名称和位置,第二个参数定义文件被打开的模式。

模式标志描述
ios::app追加模式。所有写入都追加到文件末尾
ios::ate文件打开定位到文件末尾
ios::in打开文件用于读取
ios::out打开文件用于写入
ios::trunc如果该文件已经存在,其内容将在打开文件之前被截断,即文件长度设为0
ios::binary以二进制模式进行IO模式

模式混合使用。例如,如果想要以写入模式打开文件,并希望截断文件,以防文件已存在,可以使用:

    ofstream outfile;
    outfile.open("seq_data.txt", ios::out | trunc);

2.关闭文件

当C++程序终止时,它会自动刷新所有的流,释放所分配的内存,并关闭所有打开的文件。程序员应该养成一个好习惯,在程序终止前关闭所有打开的文件。
下面是 close() 函数的标准语法,与open()函数相同,close() 函数也是 fstream、ifstream 和 ofstream 对象的一个成员。

infile.close();
outfile.close();

3.一个文件两个名称

程序使用的每个输入和输出文件都有两个名称。外部文件名是文件真实名称,但只在open函数中调用一次,该函数将文件链接到一个流。一旦调用了open,就必须将流名称作为文件名使用。

4.写入文件

文件的写入

  1. 流操作符<<
  2. put() //只能读取单个字符
  3. writer((const char*)&var,sizeof(var)); 左参数为写入的变量,右参数为写入的字节数

普通输入模式写入文件例子:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
char data[100];

//以输入模式开启seq_data.txt
ofstream outfile;
outfile.open("seq_data.txt");

cout << "Please input:\n";
cin >> data;

//  向文件写入数据
outfile << data <<endl;

//  关闭打开的文件
outfile.close();


return 0;
}

追加模式写入文件例子:

 #include <iostream>
#include <fstream>
using namespace std;

int main()
{
char data[100];

    //  以追加写入模式打开文件
ofstream outfile;
outfile.open("seq_data.txt",ios::app);

cin >> data;
cout <<data;
outfile << data <<endl;

//  关闭打开的文件
outfile.close();


return 0;
}

5.读取文件

读取文件例子:

   #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    char data[100];
    
    //  以读模式打开文件
    ifstream infile;
    infile.open("seq_data.txt");
    
    infile >> data;
    
    //  屏幕上写入数据
    cout << data <<endl;
    
    //  关闭打开的文件
    infile.close();
    
    return 0;
    }

6.检查文件是否成功打开

既然文件打开了,但是文件是否打开成功了呢?我们又该如何判断呢?有的会员会说,看看open函数的返回值,返回 false 肯定就表明打开失败了吧?但是open函数的返回值是 void 类型哦,也就是说无返回值,你又该如何判断呢?方法有多种,这里面一一列举给大家,大家喜欢用哪种方式就用哪种:
①、直接 if 判断 fs 对象;
②、用 is_open 方法判断;
③、用 good 方法判断;
④、用 fail 方法判断;
比较常用的还是前两种方法,意思表达明确。建议大家用前两种方法检测文件是否打开成功。

outfile.open("X:\\seq_txt");
	if (!outfile)
	{
		cout << "open file error." << endl;
		return 0;
	}
outfile.open("X:\\seq_txt");
	if (!outfile.is_open())
	{
		cout << "open file error." << endl;
		return 0;
	}
outfile.open("X:\\seq_txt");
	if (!outfile.good())
	{
		cout << "open file error." << endl;
		return 0;
	}
outfile.open("X:\\seq_txt");
if(outfile.fail())
{
	cout << "output file opening failed.\n";
	return 0;
}

7.二进制文本文件的读写

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char data[100];

	ofstream outfile;	//创建输出流文件
	outfile.open("D://seq_data.txt");	//打开文件

	cout << "Please input:";
	cin >> data;	//写入数据 

	//outfile << data;
	outfile.write((char*)&data, sizeof(char)*100);	//二进制写入文件,左参数代表内存的起始位置,
													//右参数为写入多少个字节
	outfile.close();



	ifstream infile;	//与写入文件方法类似
	infile.open("D:\\seq_data.txt");
	//infile >> data;
	infile.read((char*)&data, sizeof(char) * 100);
	cout << data << endl;
	return 0;
}

8.文件的写入与读取

文件的写入

  1. 流操作符<<
  2. put() //只能读取单个字符
  3. writer((const char*)&var,sizeof(var)); 左参数为写入的变量,右参数为写入的字节数

文件的读取
4. 流操作符>>
5. get() //只能读取单个字符
6. getline(szbuf,100); //左参数为缓冲区,右参数为读取的字节数
7. read((const char*)&var,sizeof(var)); 左参数为写入的变量,右参数为写入的字节数

文本文件读写代码实例

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char ch;
	//开始写
	ofstream outfile;
	outfile.open("d:\\123.txt", ios::out);
	if (!outfile)return 0;	//判断文件是否打开

	cin >> ch;

	//while(ch != '\n')	error:提取操作符>>会自动忽略空白以及换行符

	while (ch != '#')	//此处可以自定义结束符号,或使用cin.get(ch)
	{
		outfile << ch;
		cin >> ch;
	}
	outfile.close();

	//开始读
	ifstream infile;
	infile.open("d:\\123.txt", ios::in);
	if (!infile)return 0;	//判断文件是否打开

	//while (!infile.eof())	//判断是否到达文件末尾
	//{
	//	infile >> ch;
	//	cout.put(ch);
	//}
	char szbuff[100] = { 0 };

	memset(szbuff, 0, 100);	//若szbuff中已存在数据,可以使用memset函数清除缓冲区
							//第一个参数为操作对象,第二个参数为赋给操作对象的值,,第三个参数是操作对象的长度
	infile.getline(szbuff, 100);
	cout << "读取数据:" << szbuff << endl;
	infile.close();
	return 0;
}

二进制文件的读写

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char data[100];

	ofstream outfile;	//创建输出流文件
	outfile.open("D://seq_data.txt");	//打开文件

	cout << "Please input:";
	cin >> data;	//写入数据 

	outfile.write((char*)&data, sizeof(char) * 100);	//二进制写入文件,左参数代表内存的起始位置,
													//右参数为写入多少个字节
	outfile.close();



	ifstream infile;	//与写入文件方法类似
	infile.open("D:\\seq_data.txt");
	infile.read((char*)&data, sizeof(char) * 100);
	cout <<"读取数据:"<< data << endl;
	return 0;
}

9.判断文件的写入和读取是否成功

①、文件读写是否成功?
可以使用 good、bad、fail 来进行判断!
fail() 方法用于判断最后一次读取数据的时候是否遇到了类型不配的情况,若是返回true(如果遇到了EOF,该方法也返回true)
bad() 如果出现意外的问题,如文件受损或硬件故障,最后一次读取数据的时候发生了这样的问题,方法 bad() 将返回true。
good() 该方法在没有发生任何错误的时候返回true。该方法也指出的最后一次读取输入的操作是否成功。

②、读取文件的时候是否已经读到文件末尾?
可以使用 eof 来进行判断!eof() 方法用于判断最后一次读取数据的时候是否遇到EOF,即到达文件末尾,若是则返回true。
代码实例:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	//开始写
	ofstream outfile;
	outfile.open("d:\\123.txt");
	if (!outfile)return 0;

	int var = 2000;
	outfile << var << endl;
	outfile << var + 1 << endl;
	outfile.close();

	//开始读
	ifstream infile;
	infile.open("d:\\123.txt");

	var = 0;
	//第一次读取
	infile >> var;
	bool read_is = infile.good();
	bool end_of_file = infile.eof();
	cout << var << "," << "read_is=" << read_is << ",end_of_file=" << end_of_file << endl;

	//第二次读取
	infile >> var;
	read_is = infile.good();
	end_of_file = infile.eof();
	cout << var << "," << "read_is=" << read_is << ",end_of_file=" << end_of_file << endl;

	//第三次读取
	infile >> var;
	read_is = infile.good();
	end_of_file = infile.eof();
	cout << var << "," << "read_is=" << read_is << ",end_of_file=" << end_of_file << endl;
	return 0;
}

输出结果:

2000, read_is = 1, end_of_file = 0	
2001, read_is = 1, end_of_file = 0	
2001, read_is = 0, end_of_file = 1

第一次读取正常,没有读到末尾
第二次读取正常,没有读到末尾
第三次读取失败,未读到数据

10.文件指针

这里面说的文件指针也可以理解为文件内部记录读取或者写入的当前位置,不然程序如何知道下一次该从文件的什么位置开始读取或者写入呢?
在读写文件的时候,每读取或者写入一个字节,磁盘中的文件指针就会向后移动一个字节。可以通过控制指针的位置,以便在我们需要的位置进行读写文件。

文件流提供以下成员函数来读取或配置文件指针:
tellg() 返回读取文件指针的当前位置
tellp() 返回写入文件指针的当前位置
seekg(指针偏移量) 将读取文件指针移到指定位置
seekg(指针偏移量,参照位置) 将读取文件指针移到指定位置
seekp(指针偏移量) 将写入文件指针移到指定位置
seekp(指针偏移量,参照位置) 将写入文件指针移到指定位置
这些成员函数名和参数,容易混淆,下面就简单解释一下:

备注:以上函数中的最后一个字母不是g就是p,代表什么意思呢?其中,g代表get,表示读取;p代表put,表示写入。
另外,函数参数中的“文件中的位置”和“指针偏移量”为 long整型,以字节为单位。“参照位置”是一个有以下值的枚举:
ios::beg 文件开头计算偏移量
ios::cur 文件当前位置计算偏移量
ios::end 文件结尾计算偏移量
其中,函数seekg(指针偏移量) 和 seekp(指针偏移量),默认从文件开头计算偏移量。

文件指针代码实例

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	//开始写
	ofstream outfile;
	outfile.open("d:\\123.txt");
	if (!outfile)return 0;

	int var = 2000;
	outfile << var << endl;
	outfile << var + 1 << endl;
	outfile.close();

	//开始读
	ifstream infile;
	infile.open("d:\\123.txt");
	if (!infile)return 0;

	var = 0;
	//第一次读取
	int read_ptr = infile.tellg();	//第一次读取指针位置,0,因为从头开始
	infile >> var;
	bool read_is = infile.good();
	bool end_of_file = infile.eof();
	cout << var << "," << "read_is=" << read_is << ",end_of_file=" << end_of_file << endl;
	read_ptr = infile.tellg();	//第二次读取指针位置,指针位置为4,因为读取了一个数据2000
	//infile.seekg(read_ptr + 1);	//第二次读取后,指针指向CR,+1后指针指向LF,所以读取到的数据是2001
	//infile.seekg(read_ptr + 2);	//+2后指针指向2,所以读取的数据为2001
	//infile.seekg(read_ptr + 3);	//+3后指针指向第一个0,所以读取到001,因为var为int型的数,所以读取到的数据为1

	//第二次读取
	infile >> var;
	read_is = infile.good();
	end_of_file = infile.eof();
	cout << var << "," << "read_is=" << read_is << ",end_of_file=" << end_of_file << endl;
	read_ptr = infile.tellg();	//第二次读取,指针位置为12,因为2000和2001八个字节,加上两个CR(回车)、LF(换行),
								//加起来12个字节,刚好和文件的大小对应

	//第三次读取
	infile >> var;
	read_is = infile.good();
	end_of_file = infile.eof();
	cout << var << "," << "read_is=" << read_is << ",end_of_file=" << end_of_file << endl;
	read_ptr = infile.tellg();

	文件指针的应用(测量文件的大小)
	//infile.seekg(0, ios::end);	//将文件指针设置到文件末尾
	//int file_size = infile.tellg();	//返回文件指针的位置
	//cout << "file_size=" << file_size << "kb" << endl;	//此时文件指针的位置即文件的大小
	return 0;
}

注:
这段代码中文件的二进制形式为:
2000CRLF
2001CRLF
CR为回车,LF为换行,这两种都各占一个字节

三、实战练习:通过文件读写的方式自己实现一个函数,实现文件的拷贝功能

#include <iostream>
#include <fstream>
using namespace std;
int file_copy()
{
	char before_copy_route[20];
	char after_copy_route[20];

	cout << "\t\t\t\t欢迎使用菜鸟编程——文件复制( ̄▽ ̄)" << endl;
	getchar();
	cout << "请输入复制前的路径:";
	cin >> before_copy_route;
	cout << "请输入复制后的路径:";
	cin >> after_copy_route;

	ifstream infile;	//输入流的创建
	infile.open(before_copy_route, ios::in | ios::binary);	//二进制方式打开原始文件
	if (!infile)return 0;	//判断原始文件是否打开成功

	infile.seekg(0, ios::end);
	int file_len = (int)infile.tellg();	//获取文件大小

	char* buff = new char[file_len];	//创建对应大小的buff缓冲区
	infile.seekg(0);	//将文件指针设置到开头

	infile.read(buff, file_len);	//将文件内容写入buff缓冲区

	ofstream outfile;
	outfile.open(after_copy_route, ios::out | ios::binary);	//二进制打开新文件
	if (!outfile)return 0;

	outfile.write(buff, file_len);	//将缓冲区buff内容写入新文件
	infile.close();
	outfile.close();
	delete[]buff;
	return 1;
}
int main()
{
	if (file_copy())
		cout << "复制成功";
	else
		cout << "复制失败";
	getchar();
	getchar();
	return 0;
}

由于水平有限,本博客难免会有不足,恳请大佬们不吝赐教!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 一、ASCII 输出   为了使用下面的方法, 你必须包含头文件<fstream.h>(译者注:在标准C++中,已经使用<fstream>取 代< fstream.h>,所有的C++标准头文件都是无后缀的。)。这是 <iostream.h>的一个扩展集, 提供有缓 冲的文件输入输出操作. 事实上, <iostream.h> 已经被<fstream.h>包含了, 所以你不必包含所有这两个 文件, 如果你想显式包含他们,那随便你。我们从文件操作类的设计开始, 我会讲解如何进行ASCII I/O 操作。如果你猜是"fstream," 恭喜你答对了! 但这篇文章介绍的方法,我们分别使用"ifstream"?和 "ofstream" 来作输入输出。   如果你用过标准控制台"cin"?和 "cout," 那现在的事情对你来说很简单。 我们现在开始讲输出部 分,首先声明一个类对象。 ofstream fout;   这就可以了,不过你要打开一个文件的话, 必须像这样调用ofstream::open()。 fout.open("output.txt");   你也可以把文件名作为构造参数来打开一个文件. ofstream fout("output.txt");   这是我们使用的方法, 因为这样创建和打开一个文件看起来更简单. 顺便说一句, 如果你要打开的文 件不存在,它会为你创建一个, 所以不用担心文件创建的问题. 现在就输出到文件,看起来和"cout"的操 作很像。 对不了解控制台输出"cout"的人, 这里有个例子。 int num = 150; char name[] = "John Doe"; fout << "Here is a number: " << num << " "; fout << "Now here is a string: " << name << " ";   现在保存文件,你必须关闭文件,或者回写文件缓冲. 文件关闭之后就不能再操作了, 所以只有在你 不再操作这个文件的时候才调用它,它会自动保存文件。 回写缓冲区会在保持文件打开的情况下保存文 件, 所以只要有必要就使用它。回写看起来像另一次输出, 然后调用方法关闭。像这样: fout << flush; fout.close();    现在你用文本编辑器打开文件,内容看起来是这样:   Here is a number: 150 Now here is a string: John Doe   很简单吧! 现在继续文件输入, 需要一点技巧, 所以先确认你已经明白了操作,对 "<<" 和">>" 比较熟悉了, 因为你接下来还要用到他们。继续…   二、ASCII 输入   输入和"cin" 很像. 和刚刚讨论的输出很像, 但你要考虑几件事情。在我们开始复杂的内容之前 , 先看一个文本:   12 GameDev 15.45 L This is really awesome!   为了打开这个文件,你必须创建一个in-stream对象,?像这样。 ifstream fin("input.txt");   现在读入前四行. 你还记得怎么用"<<" 操作符往里插入变量和符号吧?好,?在 "<<" (插入)?操作 符之后,是">>" (提取) 操作符. 使用方法是一样的. 看这个代码片段.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值