写在前面:本文章来自于在学习过程中的总结,供大家参考。因水平有限,博客中难免会有不足,恳请大佬们不吝赐教!
相关文章:
[C++系列]小白的C++入门教程
C++流IO工具
C++字符IO
文章目录
一、流和基本文件I/O
流是由字符(或其他类型的数据)构成的“流”。流向程序,称为输入流。流出程序,称为输出流。如cin是连接键盘的输入流,cout是连接屏幕的输出流。
1.文件I/O
C++的文件操作就是指对文件进行读写的操作,那么 IO 呢?字母 I 就是 Input 的缩写,意为输入,字母 O 就是 Output 的缩写,意为输出。所以文件的 IO 操作就是指文件的输入与输出。输入就是从磁盘上的文件中读取内容到内存中。输出就是将内存中的数据内容输出或者说写入到磁盘的文件中。(也就是说,输入和输出是相对于内存中的数据而言的)
fstream头文件定义了三种支持文件IO的类型:
- ifstream,由istream派生而来,提供读文件的功能。
- ofstream,由ostream派生而来,提供写文件的功能。
- 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.写入文件
文件的写入:
- 流操作符<<
- put() //只能读取单个字符
- 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.文件的写入与读取
文件的写入:
- 流操作符<<
- put() //只能读取单个字符
- 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;
}
由于水平有限,本博客难免会有不足,恳请大佬们不吝赐教!