C++文件操作

1. 文件的概述

文件,一般是指相关数据的集合。计算机中的数据都是以文件的形式放置在外部介质(譬如磁盘、光盘和U盘)中的。

操作系统是以文件为单位对数据进行管理的,也就是说:

  • 想找外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据

  • 想向外部介质上存储数据,必须先建立一个文件(以文件名标识你的数据要存到介质的哪个地方)然后把数据输出到那里

1.1 文件的分类 之一

  • 外部文件

是指磁盘文件、光盘文件、U盘文件。目前使用最为广泛的是磁盘文件,在程序中对光盘文件和U盘文件的访问与磁盘文件相同。

  • 内部文件

是指程序运行中的文件,更正式的称谓是“文件流对象”。


对用户来说,常用 的文件有两大类:

  1. 程序文件(program file)如C++的源代码文件(.cpp)、目标文件(.obj)、可执行文件(.exe)等

  2. 数据文件(data file) 在程序运行时,常常需要将一些数据(运行的最终结果或者中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机的内存。这种磁盘文件称为数据文件。

    程序中的输入和输出的对象就是数据文件。

1.2 文件的分类之二

  • ASCII文件:也叫文本文件、字符文件,每个字节存放一个ASCII代码,表示一个字符

  • 二进制文件:也叫内部格式文件、字节文件,把内存中的数据按其在内存中的存储格式原样输出到磁盘上存放

对于字符信息,其在内存中是以ASCII代码的形式存放的,因此,无论用ASCII文件输出还是二进制文件输出,其数据形式都是一样的,不会出现“乱码”现象。

对于数值数据和其他数据,二者是不同的,例如有一个int类型的数据100000,其在内存中占4个字节,如果按内存格式输出,在磁盘文件中占4个字节,如果将它转化为ASCII码形式输出(便于人类观看),则要占6个字节。

“类型”规定了你如何去解释那串二进制代码!

1.3. C++如何使用文件

输入和输出都是针对内存而言的
文件里面的内容 在C++看来是字符流或二进制流(类比于一个水库中的水),统称为文件流。

内存里面的内容 在C++看来也应该是流(类比于工厂中要用的水或排除出的水),鄙人将其理解字节流。

所以要将文件里面的内容弄到内存中去,或者要将内存中的内容弄到文件里面去,就必须有一根管子连接。

管子的一端始终接着内存,另外一端接着文件(这一段可以改变,也就是说,你可以先接这个文件,再接那个文件)

1.4.文件流类和文件流对象(管子类和管子对象

  • 输出文件流:内存-------->外部文件

  • 输入文件流:外部文件--------->内存

每个文件流都有一个内存缓冲区与之对应。

C++的I/O库中定义了3个专门用于文件操作的“管子类”:

  • ifstream

  • ofstream

  • fstream

1
2
3
4
5
#include <iostream>
#include <fstream>
// 定义“管子”对象的同时 也就规定了流的方向
ifstream infile; // 外部文件-------->内存
ofstream outfile; // 内存-------->外部文件

2.文件的打开与关闭

2.1 打开文件 open( )

三种“管子”对象都能够打开文件,

  • ifstream类的实例 打开文件进行写

  • ofstream类的实例 打开文件进行读

  • fstream类的实例 打开文件可读可写

1
2
3
4
5
6
7
8
9
10
11
12
13
// example
ofstream outfile;
outfile.open("myFile.txt",ios::out);
if(outfile.open("myFile.txt",ios::out)==0){// 打开文件失败 open函数返回
cerr<<"open erro!";
return 0;
}

ofstream outfile1("myFile.txt",ios::out); // 作用同上
if(!outfile1){
cerr<<"open erro!";
return 0;
}

文件输入输出方式设置值

方式 作用
ios::in 以输入方式打开文件,准备读文件
ios::out 以输出方式打开文件(这是默认方式)如果已有此文件,则将原来的内容清空后;文件不存在,则创建之
ios::app 以输出方式打开文件,写入的数据添加再文件末尾;文件不存在,则创建之
ios::binary 以二进制方式打开文件,如果不指定这个,则默认为ASCII方式(即文本模式)
ios::trunc 打开一个文件,如果文件存在,则删除其中全部数据,如不存在,则新建新文件
ios::ate 打开一个已有文件,文件指针指向文件末尾

2.2 关闭文件close( )

1
outfile.close();

当C++程序终止时,它会自动关闭刷新所有的流,释放所有分配的内存,并关闭所有打开的文件,但一个合格的程序员应该在程序终止前自己关闭所有你打开的文件(你得为你所作的负责)。

所谓“关闭”,实际上是解除“管子”和磁盘文件的关联,让“管子”的一端不和磁盘文件相连了。这样,原来的设置方式也会失效。

3. 文件的读写

如果文件中的每1字节中均以ASCII代码形式存放数据,即1字节存放一个字符,这个文件就是ASCII文件(或称为文本文件)。

二进制文件在操作时,数据不做任何的变换,直接传送,因此它又被称为内存数据的映像文件。因为文件中的信息不是字符数据,而是字节中的二进制形式的信息,因而它也被称为字节文件


3.1 文本文件的写入


【example 1 start】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc,char* argv)
{
int a;
string s;
cout<<"Please input a filename:"<<endl;
cin>>s;
ofstream outfile(s,ios::out);

if(!outfile){
cerr<<"Open erro!"<<endl;
exit(1);
}
cin>>a;
string temp;
cin>>temp;
outfile<<a<<temp<<endl;
outfile.close();
return 0;
}

运行上面的代码:

得到1.txt文件,其内容如下:

看看1.txt的属性,应该为9个字节(3+2+2+2)

解释:

  • 3 321 共3个字符占3个字节

  • 2 ax 共2个字符占2个字节

  • 2 你 共1个字符,由于是中文,所以占2个字节

  • 2 0D 0A 对应于\r \n,即后面的endl 共占2个字节

看看1.txt在二进制模式下的内容:

思考:

1
cin>>a;// 用户在输入a时超过了int类型的范围会怎么样?后面的string类型能写入文件嘛?

answer: 会将int的最大数写入文件,后面的string类型写不进文件。

【example 1 end】


3.2 文本文件的读出

【example 2 start】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc,char* argv)
{
string s;
cout<<"Please input a filename:"<<endl;
cin>>s;
ifstream infile(s,ios::in);

if(!infile){
cerr<<"Open erro!"<<endl;
exit(1);
}

char ch;
while(infile.get(ch)){
cout<<ch;
}
cout<<endl;

infile.close();
return 0;
}

【example 2 end】


3.3 二进制文件的写入


【example 3 start】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <fstream>
using namespace std;
struct Greece{
char name[20];
float up;
float weight;
char addr[50];
};
int main(int argc,char* argv)
{
Greece gre[3] = {
"白菜",3.5,5.0,"上海",
"茄子",4.5,7.0,"北京",
"萝卜",6.5,3.0,"湖南"
};
ofstream outfile;
string fileName;
cout<<"Please input a filename:"<<endl;
cin>>fileName;
try{
outfile.open(fileName,ios::binary);
for(int i=0;i<3;i++){
outfile.write((char*)&gre[i],sizeof(gre[i]));
}
outfile.close();
}catch(exception& e){
cerr<<"Exception!"<<e.what()<<endl;
}
return 0;
}

运行上面的代码:

得到2.txt文件,其内容会有乱码,如下所示:

分析得到其大小应该为240个字节(80*3)(注意内存对齐现象)

查看2.txt的属性:

【example 3 end】


3.4 二进制文件的读出


【example 4 start】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <fstream>
using namespace std;
struct Greece{
char name[20];
float up;
float weight;
char addr[50];
};
int main(int argc,char* argv)
{
Greece gre[3] = {
};
ifstream infile;
string fileName;
cout<<"Please input a filename:"<<endl;
cin>>fileName;
try{
infile.open(fileName,ios::binary);
for(int i=0;i<3;i++){
infile.read((char*)&gre[i],sizeof(gre[i]));
}
infile.close();
}catch(exception& e){
cerr<<"Exception!"<<e.what()<<endl;
}

for(int i=0;i<3;i++){
cout<<"NO."<<i+1<<endl;
cout<<"名称:"<<gre[i].name<<endl;
cout<<"单价:"<<gre[i].up<<endl;
cout<<"重量:"<<gre[i].weight<<endl;
cout<<"产地:"<<gre[i].addr<<endl;
}
return 0;
}

运行上述代码:

【example 4 end】


3.5 文件的数据定位

磁盘文件中有一个文件指针,用来指明当前应进行的读写的位置。

  • 在输入时每读入1个字节,指针就后移1个字节

  • 在输出时每输出1个字节,指着就后移1个字节

对应二进制文件,允许对指针进行控制,所以提供了一些函数供我们使用。

成员函数 作用
gcount( ) 返回最后一次输入所读入的字节数
tellg( ) 返回输入文件指针的当前位置
seekg(文件中的位置) 将输入文件中指针移到指定的位置
seekg(位移量,参照位置) 以参照位置为基础移动若干字节
tellp( ) 返回输出文件指针的当前位置
seekp(文件中的位置) 将输出文件中指针移到指定的位置
seekp(位移量,参照位置 ) 以参照位置为基础移动若干字节

参照位置可取:

  • ios::beg

  • ios::cur

  • ios::end

文章作者: 小王同学
文章链接: https://morvan.top/2015/10/03/C++FileOperator/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 小王同学的精神驿站