C++之文件IO操作流

标签: 文件 io | 发表时间:2011-08-18 19:23 | 作者:Rookie_J Nanqi
出处:http://www.cnblogs.com/

  前两节介绍了C++的IO流类库,标准设备IO操作流中部分预定义流对象的成员函数以及IO格式控制。那今天我将继续介绍关于C++中的流操作内容——文件IO操作流fstream。并会着重讲解C++是如何对文件进行操作的。

  文件指存放在外部介质上的数据的集合。大家都知道操作系统是以文件为单位来对数据进行管理的。因此如果你要查找外部介质的数据,则先要按文件名找到指定文件,然后再从文件中读取数据,如果要把数据存入外部介质中,如果没有该文件,则先要建立文件,再向它输入数据。由于文件的内容千变万化,大小各不相同,为了统一处理,在C++中用文件流的形式来处理,文件流是以外存文件为输入输出对象的数据流。输出文件流表示从内存流向外存文件的数据,输入文件流则相反。根据文件中数据的组织形式,文件可分为两类:文本文件和二进制文件。文本文件又称为ASCII文件,它的每个字节存放一个ASCII码,代表一个字符。二进制文件则是把内存中的数据,按照其在内存中的存储形式原样写在磁盘上存放。比如一个整数20000,在内存中在两个字节,而按文本形式输出则占5个字节。因此在以文本形式输出时,一个字节对应一个字符,因而便于字符的输出,缺点则是占用存储空间较多用二进制形式输出数据,节省了转化时间和存储空间,但不能直接以字符的形式输出

  1.在C++中对文件进行操作分为以下几个步骤:(1)建立文件流对象;(2)打开或建立文件;(3)进行读写操作;(4)关闭文件;用于文件IO操作的流类主要有三个fstream(输入输出文件流),ifstream(输入文件流)和ofstream(输出文件流);而这三个类都包含在头文件fstream中,所以程序中对文件进行操作必须包含该头文件。首先建立流对象,然后使用文件流类的成员函数open打开文件,即把文件流对象和指定的磁盘文件建立关联。成员函数open的一般形式为:

  文件流对象.open(文件名,使用方式);

其中文件名可以包括路径(如:e:\c++\file.txt),如果缺少路径,则默认为当前目录。使用方式则是指文件将被如何打开。以下就是文件的部分使用方式,都是ios基类中的枚举类型的值:

此外打开方式有几个注意点:

(1)因为nocreate和noreplace,与系统平台相关密切,所以在C++标准去掉了对它的支持。

(2)每一个打开的文件都有一个文件指针,指针的开始位置由打开方式指定,每次读写都从文件指针的当前位置开始。每读一个字节,指针就后移一个字节。当文件指针移到最后,会遇到文件结束符EOF,此时流对象的成员函数eof的值为非0值,表示文件结束。

(3)用in方式打开文件只能用于输入数据,而且该文件必须已经存在。

(4)用app方式打开文件,此时文件必须存在,打开时文件指针处于末尾,且该方式只能用于输出。

(5)用ate方式打开一个已存在的文件,文件指针自动移到文件末尾,数据可以写入到其中。

如果文件需要用两种或多种方式打开,则用"|"来分隔组合在一起。除了用open成员函数打开文件,还可以用文件流类的构造函数来打开文件,其参数和默认值与open函数完全相同。比如:文件流类 stream(文件名,使用方法);如果文件打开操作失败,open函数的返回值为0,用构造函数打开的话,流对象的值为0。所以无论用哪一种方式打开文件,都需要在程序中测试文件是否成功打开

   在每次对文件IO操作结束后,都需要把文件关闭,那么就需要用到文件流类的成员函数close,一般调用形式:流对象.close();关闭实际上就是文件流对象和磁盘文件失去关联。

  2.介绍完文件的打开和关闭,接下来说说文件的读写。我将分别从文本文件读写和二进制文件的读写来介绍。其实文件的读写是十分容易的。流类库中的IO操作<<、>>、put、get、getline、read和write都可以用于文件的输入输出。

  (1)文本文件的读写

写文件:

 1 #include "stdafx.h"
2 #include <iostream>
3 #include <fstream>
4
5 int main()
6 {
7 //打开文件
8 std::ofstream file("file.txt",std::ios::out|std::ios::ate);
9 if(!file)
10 {
11 std::cout<<"不可以打开文件"<<std::endl;
12 exit(1);
13 }
14
15 //写文件
16 file<<"hello c++!\n";
17
18 char ch;
19 while(std::cin.get(ch))
20 {
21 if(ch=='\n')
22 break;
23 file.put(ch);
24 }
25
26 //关闭文件
27 file.close();
28
29 return 0;
30 }

键盘输入字符:

读文件file.txt:

 1 #include "stdafx.h"
2 #include <iostream>
3 #include <fstream>
4
5 int main()
6 {
7 //打开文件
8 std::ifstream rfile("file.txt",std::ios::in);
9 if(!rfile)
10 {
11 std::cout<<"不可以打开文件"<<std::endl;
12 exit(1);
13 }
14
15 //读文件
16 char str[100];
17 rfile.getline(str,100);//读到'\n'终止
18 std::cout<<str<<std::endl;
19
20 char rch;
21 while(rfile.get(rch))//文件指针指向字符‘\n’的下一个
22 {
23 std::cout.put(rch);
24 }
25
26 std::cout<<std::endl;
27
28 //关闭文件
29 rfile.close();
30
31 return 0;
32 }

读出显示字符:

其实建立ifstream类和ofstream类的对象时,ios:in和ios:out可以省略,因为ifstream类默认为ios:in,ofstream类默认为ios:out;

  (2)最初设计流的目的是用于文本,因此在默认情况下,文件用文本方式打开。在以文本模式输出时,若遇到换行符"\n"(十进制为10)则自动扩充为回车换行符(十进制为13和10)。所以,如果我们输入的整数10,那么在文件输出时会转化为13和10,然而这并不是我们所需要的。为了解决这样的问题,就要采用而二进制模式,使其所写的字符不转换。在对二进制文件进行IO操作时,打开文件时要指定方式ios::binary,即以二进制形式传送和存储。接下来我用read函数和write函数来对二进制文件进行读写。在示例描述之前先简单介绍一下这两个函数:

  read函数常用格式为:文件流对象.read(char *buf,int len);

  write函数常用格式为:文件流对象.write(const char *buf,int len);

两者格式上差不多,第一个参数是一个字符指针,用于指向读入读出数据所放的内存空间的其实地址。第二个参数是一个整数,表示要读入读出的数据的字节数。以下是二进制文件的读写的示例:

定义一个精灵类(用于文件数据处理):

 1 class Sprite
2 {
3 private:
4 std::string profession;//职业
5 std::string weapon;//武器
6 static int count;//个数
7 public:
8 Sprite(){}
9 Sprite(std::string profession,std::string weapon):profession(profession),weapon(weapon)
10 {
11 }
12 void showSprite();//显示精灵信息
13 };
14
15 int Sprite::count=0;
16
17 void Sprite::showSprite()
18 {
19 ++count;
20 std::cout<<"精灵"<<count<<" 职业:"<<profession<<" 武器:"<<weapon<<std::endl;
21 }

写文件:

 1 #include "stdafx.h"
2 #include <iostream>
3 #include <fstream>
4 #include <string>
5
6 int main()
7 {
8 //建立对象数组
9 Sprite sprites[3]={
10 Sprite("法师","魔杖"),
11 Sprite("战士","屠龙宝刀"),
12 Sprite("道士","倚天剑")
13 };
14
15 //打开文件
16 std::ofstream file("file.dat",std::ios::ate|std::ios::binary);
17 if(!file)
18 {
19 std::cout<<"文件打开失败!";
20 abort();//等同于exit
21 }
22
23 //写文件
24 for(int i=0;i<3;i++)
25 file.write((char*) &sprites[i],sizeof(sprites[i]));
26
27 //关闭文件
28 file.close();
29
30 return 0;
31 }

读文件file.dat:

 1 #include "stdafx.h"
2 #include <iostream>
3 #include <fstream>
4 #include <string>
5
6 int main()
7 {
8 //建立对象数组
9 Sprite rsprites[3];
10
11 //打开文件
12 std::ifstream rfile("file.dat",std::ios::binary);
13 if(!rfile)
14 {
15 std::cout<<"文件打开失败!";
16 return 1;//等同于exit
17 }
18
19 //读文件
20 for(int i=0;i<3;i++)
21 {
22 rfile.read((char*) &rsprites[i],sizeof(rsprites[i]));
23 rsprites[i].showSprite();
24 }
25
26 //关闭文件
27 rfile.close();
28
29 return 0;
30 }

读出显示字符:

在read函数还是write函数里都要把数据转化为char*类型,代码中sizeof函数是用于确定要读入读出的字节数。

  在文件结束处有个标志位EOF,在用文件流读取文件时,使用成员函数eof()(函数原型:int eof())可以检测到结束符。如果该函数返回值为非零,则表示到达文件末尾。返回零则表示未达到文件末尾。

  (3)前面所介绍的文件都是按顺序来读取的的,C++中又提供了针对于文件读写指针的相关成员函数,使得我们可以在IO流中随意移动文件指针,从而对文件的进行随机地读写。类istream针对读指针提供3个成员函数:

  tellg()//返回输入文件读指针的当前位置;

  seekg(文件中的位置)//将输入文件中的读指针移动到指定位置

  seekg(位移量,参照位置)//以参照位置为基准移动若干字节

其中参照位置是枚举值:

beg//从文件开头计算要移动的字节数

cur//从文件指针的当前位置计算要移动的字节数

end//从文件的末尾计算要移动的字节数

如果参照位置省略,则默认为beg。而类ostream针对写指针提供的3个成员函数:

  tellp()//返回输出文件写指针的当前位置;

  seekp(文件中的位置)//将输出文件中的写指针移动到指定位置

  seekp(位移量,参照位置)//以参照位置为基准移动若干字节

现在我对上一示例中读取二进制文件代码稍作更改:

 1 #include "stdafx.h"
2 #include <iostream>
3 #include <fstream>
4 #include <string>
5
6 int main()
7 {
8 //建立对象数组
9 Sprite rsprites[3];
10
11 //打开文件
12 std::ifstream rfile("file.dat",std::ios::binary);
13 if(!rfile)
14 {
15 std::cout<<"文件打开失败!";
16 return 1;//等同于exit
17 }
18
19 //读文件
20 for(int i=0;i<3;i++)
21 {
22 rfile.read((char*) &rsprites[i],sizeof(rsprites[i]));
23 rsprites[i].showSprite();
24 }
25
26 Sprite rsprite;//建立对象
27
28 std::cout<<"改变读取顺序:"<<std::endl;
29 rfile.seekg(sizeof(Sprite)*2,std::ios::beg);//读取精灵道士信息
30 rfile.read((char*) &rsprite,sizeof(Sprite));
31 rsprite.showSprite();
32
33 rfile.seekg(-int(sizeof(Sprite)*2),std::ios::end);//读取精灵战士信息
34 rfile.read((char*) &rsprite,sizeof(Sprite));
35 rsprite.showSprite();
36
37 rfile.seekg(-int(sizeof(Sprite)*2),std::ios::cur);//读取精灵法师信息
38 rfile.read((char*) &rsprite,sizeof(Sprite));
39 rsprite.showSprite();
40
41 //关闭文件
42 rfile.close();
43
44 return 0;
45 }

结果:

作者: Rookie_J 发表于 2011-08-18 19:23 原文链接

评论: 0 查看评论 发表评论


最新新闻:
· 在专利收购战中进化的硅谷生态链:专利如货币(2011-08-19 08:47)
· 动视暴雪高管抨击EA对《使命召唤》攻击言论(2011-08-19 08:45)
· Facebook修改开发者应用管理平台用户界面(2011-08-19 08:44)
· 苹果对纽约冒牌苹果店提起诉讼 撤下销售商品(2011-08-19 08:43)
· 微软Store开店三年计划:从11家店拓展到86家(2011-08-19 08:42)

编辑推荐:你在成长为一个优秀的程序员吗

网站导航:博客园首页  我的园子  新闻  闪存  小组  博问  知识库

相关 [文件 io] 推荐:

C++之文件IO操作流

- Nanqi - 博客园-首页原创精华区
  前两节介绍了C++的IO流类库,标准设备IO操作流中部分预定义流对象的成员函数以及IO格式控制. 那今天我将继续介绍关于C++中的流操作内容——文件IO操作流fstream. 并会着重讲解C++是如何对文件进行操作的.   文件指存放在外部介质上的数据的集合. 大家都知道操作系统是以文件为单位来对数据进行管理的.

物理IO与逻辑IO

- - 操作系统 - ITeye博客
IO性能对于一个系统的影响是至关重要的. 一个系统经过多项优化以后,瓶颈往往落在数据库;而数据库经过多种优化以后,瓶颈最终会落到IO. 而IO性能的发展,明显落后于CPU的发展. Memchached也好,NoSql也好,这些流行技术的背后都在直接或者间接地回避IO瓶颈,从而提高系统性能. 上图层次比较多,但总的就是三部分.

linux异步IO浅析

- Sepher - kouu&#39;s home
知道异步IO已经很久了,但是直到最近,才真正用它来解决一下实际问题(在一个CPU密集型的应用中,有一些需要处理的数据可能放在磁盘上. 预先知道这些数据的位置,所以预先发起异步IO读请求. 等到真正需要用到这些数据的时候,再等待异步IO完成. 使用了异步IO,在发起IO请求到实际使用数据这段时间内,程序还可以继续做其他事情).

java nio和io的比较

- - 互联网 - ITeye博客
第一部分:简单介绍NIO.     服务器在合理时间内处理大量客户机的请求的能力取决于服务器使用I/O流的效率,同时为成百上千的客户提供服务的服务器必须能并发的使用I/O服务.     用Java语言写的服务器,由于其线程与客户机之比几乎是一比一,因而易受到大量线程开销的影响,其结果是即导致性能问题,又缺乏伸缩性.

异步IO一定更好吗?

- Wolf - CNode社区
在长林的文章《nodejs异步IO的实现》中提到,NodeJS通过libeio来实现IO操作的异步化,而libeio采用多线程的方式来模拟异步操作. 这里我需要强调一个观点,异步IO虽然是NodeJS一个非常重要的特点,但异步IO并不总是最好的,其他语言也一样. 在我的磁盘上有2个文件,我希望在一个程序里读取这2个文件,每次输出一个字符.

linux AIO (异步IO) 那点事儿

- zffl - CNode社区
这时候进程至少会阻塞10次,而这可能会导致其他的上千个用户请求得不到处理,这当然是不能接受的.. Linux AIO 早就被提上议程,目前比较知名的有 Glibc 的 AIO   与 Kernel Native AIO. 我们用Glibc 的AIO 做个小实验,写一个简单的程序:异步方式读取一个文件,并注册异步回调函数:.

MySQL数据库的IO操作

- - haohtml's blog
         淘宝丁奇分享的PPT:MySQL数据库的IO操作,详细分享了四块的内容,并且告诉大家如何调整MySQL数据库IO操作相关的参数,给出了详细的选择策略,现替其整理成文章分享与此. 4.影响io行为的一些参数和选择策略. 一个简单的查询 select * from t where id>=(  select id from t where k1=100 limit 100000,1) limit 2;.

定位IO瓶颈的一些方法

- - Linux - 操作系统 - ITeye博客
IO瓶颈往往是我们可能会忽略的地方(我们常会看top、free、netstat等等,但经常会忽略IO的负载情况),今天给大家详细分享一下如何确认一台服务器的IO负载是否到达了瓶颈,以及可能优化、定位的点. 先来看一台典型的IO密集型服务器的cpu统计图:. 可以看到,CPU总使用率不高,平均1.3%,max到5.6%,虽然大部分都耗在了iowait上,但才百分之五左右,应该还没到瓶颈吧.

hadoop的IO和MapReduce优化参数

- - CSDN博客系统运维推荐文章
           在MapReduce执行过程中,特别是Shuffle阶段,尽量使用内存缓冲区存储数据,减少磁盘溢写次数;同时在作业执行过程中增加并行度,都能够显著提高系统性能,这也是配置优化的一个重要依据.            下面分别介绍I/O属性和MapReduce属性这两个类的部分属性,并指明其优化方向.

Innodb IO优化-配置优化

- - OurMySQL
   对于数据库来讲大多瓶颈都出现在IO问题上,所以现在SSD类的设备也才能大行其道. 那数据库的IO这块有什么可以优化的吗. 我这里大致谈一下我的看法,希望能达到一个抛砖引玉的效果.    这里谈一下数据库本身的配置方面.    配置方面对于IO优化的原则:尽可能能缓存,减少读对数据库的随机IO的请求;同时减少写的随机IO的随时发生,利用各种buffer去缓存.