文件操作是很高频的,文件的复制,编码,格式转换等等都要用到。因此熟悉对文件的操作就显得至关重要。C++中,文件被分为两类—文本文件、二进制文件。这就涉及到两个概念——字符流和字节流;拿二进制文件举例,每个二进制文件都可以理解为是由01表示的,只不过通过计算机的编码最终展示为我们能看懂的符号、文字或者图像等。用一个形象的比喻,二进制文件可以理解为一个大水缸,0和1就比作缸里的水,而字节流则可以理解为水流,文件的读取就可以形象的形容为水一边流,程序一边读取水流中的01。
C++中是通过fstream
文件流来实现对文件的操作的,其中ifstream
,ofstream
是fstream
的派生类,分别负责输入流和输出流。当我们使用#include <fstream>
时,我们就可以使用其中的 ifstream
,ofstream
以及fstream
这三个类了,也就可以用这三个类来定义相应的对象了,例如:
1 | //定义一个fin的对象了 |
open()这个成员函数重载了好几种形式
1 | /** |
- prot属性表
代码 | 属性 |
---|---|
ios::in | 为输入(读)而打开文件 |
ios::out | 为输出(写)而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 所有输出附加在文件末尾 |
ios::trunc | 如果文件已存在则先删除该文件 |
ios::binary | 二进制方式 |
*注:这些方式是能够进行组合使用的,以“或”运算(“|”)的方式,例如:
1 | fin.open("test.txt", ios::in|ios::out|ios::binary) |
第一个参数是一个常量字符指针,你可以填入所要打开文件的相对路径,也可以填入绝对路径;当然,你也可以填入一个指向文件名字符串的指针常量,或者内容等于文件名的string常量,例如:
1 | const char* c_path = "C:\\example.txt"; |
或者
1 | const string str_path = "C:\\example.txt"; |
注:必须是指向常量的指针或string
1 | //若非const string |
重要函数
is_open()成员函数
is_open()
判断文件是否正确打开,如果是,返回true
,否则,返回false
。一般用于检测文件的路径是否正确。get()成员函数
int get()
不带参数的get成员从输入流中提取一个字符(包括空白字符),并且返回该字符作为函数的返回值。当遇到文件结束符时,返回文件结束符常量EOF(-1)
ifstream& get(char* c)
从输入流中提取一个字符(包括空白字符),并且把它放到字符引用c中。当遇到文件结束符时返回0,否则返回ifstream对象引用。
ifstream& get(char* c,int n,char = ‘\n’)
与getline相似,从输入流中读取n-1个字符(包括空白字符),并为字符串结尾添加’\0’;第三个参数便是指定分隔符,默认为’\n’
注:最容易忽略的一点,也是与getline的不同点,get不会将输入流中读到的分隔符放入数组c中,因此分隔符仍保留在输入流中,若不妥善处理可能会影响下一次的读取。
getline()成员函数
在介绍这个成员函数之前,可能有些同学会对换行有些疑问。对于换行这个动作,不同的平台有不同的表示方式,比如:
平台 字符表示 unix 用0x0A表示换行(‘\n’) mac 用回车符CR表示下一行(‘\r’) windows 0x0D和0x0A两个字符(“\r\n”) Unix系统里,每行结尾只有“<换行>”,即“\n”;
Mac系统里,每行结尾是“<回车>”,即“\r”;
Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;。
1
2
3
4
5/**
* 该函数从输入流中读取n-1个字符到c中,遇到指定的结束分隔符停止读取,结束分割符默认是'\n'
*/
ifstream& getline(char* c,int n,char = '\n');
//注:getline读取时遇到分隔符会将其从输入流中删除>>
与<<
操作符由于ifstream以及ofstream分别继承于istream和ostream,所以他们也分别继承了相应的运算符,可与cin,cout一样使用>>, <<运算符。但需要注意一点,若从输入流中读到空白字符,则认为字符串结束了。换句话说,空白字符会终止文件读取。
二进制读写
读取文件时,二进制文件与文本文件在判定文件结束标识的方法存在区别。当get()之类的成员函数遇到文件标识符时,返回常量EOF(-1)作为文件结束标志,但二进制则不能用EOF,因为若文件中某个字节的值为-1,就会被误认为结束标志。C++提供了一个成员函数eof()来解决这个问题。
get()和put()操作二进制文件
istream& get(char &ch)、ostream& put(char ch)
由于文件流是从
istream
、ostream
、iostream
类继承来的,这些类的成员函数同样可用于文件流类。因此文件操作中可用get()从输入文件流中读取字符,用put()
函数将字符插入到输出文件流中。1
2
3
4
5
6
7
8
9
10
11
12
13
14void fun(string& path,string& copyPath) {
ifstream fin(path,ios::binary);
ofstream fout(copyPath,ios::binary);
//初始化buff
char buff = '\0';
if (fin.is_open()&& fout.is_open()) {
do {
fin.get(buff);
printf("%c",buff);
fout.put(buff);
} while (!fin.eof());
printf("文件结束");
}
}
read()和write()操作二进制文件
istream& read(char* buff,int n)、ostream& write(const char* buff,int n)
二进制读写的重点就是read()
和write()
函数的使用,这两个方法允许我们以更快的方式——数据块的形式读写文件,我们需要准备一个文件缓存区char buff[1024]
——字符数组用来接受每次从数据流中读到的数据。并指定要读取的字节数,例如:
1 | //读取数据块 |
文件拷贝例子:
1 | void copy(string& path,string& copyPath) { |
另外浅显的说一下序列化和反序列化。
序列化: 将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。