最近在写哈夫曼压缩,遇到了一个比较让人头疼的问题,那就是对文件的读写操作,尤其是以二进制的形式来读写,无奈C++Primer第五版上写的并不详细,很多让人困惑的地方没有涉及或者没有讲清楚。于是这几天我一直在网上看了很多这方面相关的文章,同时自己也在电脑上试了很多,觉得理解了不少,很多困惑也迎刃而解。

我们都知道,C语言里面对文件的操作是通过文件指针,以及一些相关的函数,那么C++中是如何对文件进行操作的呢?没错,就是通过 fstream 这个文件流来实现的。第一次听到文件流这个名字时,我也是一脸懵逼,总感觉这东西太抽象了,其实我们可以简单地把它理解成一个类,这样是不是清楚多了,当我们使用#include <fstream> 时,我们就可以使用其中的 ifstream,ofstream以及fstream 这三个类了,也就可以用这三个类来定义相应的对象了,例如:

ifstream fin();

这样我们就定义了一个叫做fin的对象了,它就和我们自己定义的其他对象一样,可以调用ifstream类中的一些函数,可以使用ifstream类中定义的一些操作符等等。那么我们定义完这个对象后,怎么和某个文件关联起来呢?毕竟我们最终是希望它能帮我们实现对文件的操作。没错,我们可以调用它的一个成员函数来实现,如下:

fin.open("test.txt");

open()这个成员函数重载了好几种形式,上面这种是最简单的,它中间的参数是一个常量字符指针,你可以填入所要打开文件的相对路径(比如和你的.cpp文件在同一目录下你就可以直接输入文件名,注意必须是完整的文件名,包含文件拓展名,否则找不到这个文件的),也可以填入绝对路径(比如 "D:\CodeBlocks\Myprojects\0407test\test.txt"),当然,你也可以填入一个指向文件名字符串的指针常量,或者内容等于文件名的string常量,例如:

const char * c = "test.txt";

fin.open(c);

或者

const string s = "test.txt";

fin.open(s);

注意必须是指向常量的指针或string

如果不是const string 那么得这样用

string s = "test.txt";

fin.open(str.c_str()); 这里的c_str()是 string 的一个成员函数,作用就是变成一个const string ,其实也就相当于上面的了

另外,open()函数里还可以加上其他参数,例如打开的方式,等等。但是文件名是最基本的一个,如果其他参数未加,将使用缺省值,这个函数的声明,

void open ( const char * filename,
ios_base::openmode mode = ios_base::in | ios_base::out ); void open(const wchar_t *_Filename,
ios_base::openmode mode= ios_base::in | ios_base::out,
int prot = ios_base::_Openprot);

参数:

filename    操作文件名

mode        打开文件的方式

prot          打开文件的属性   //基本很少用到,在查看资料时,发现有两种方式

ios::in 为输入(读)而打开文件
ios::out 为输出(写)而打开文件
ios::ate 初始位置:文件尾
ios::app 所有输出附加在文件末尾
ios::trunc 如果文件已存在则先删除该文件
ios::binary 二进制方式

这些方式是能够进行组合使用的,以“或”运算(“|”)的方式,例如:

fin.open("test.txt", ios::in|ios::out|ios::binary)

另外,这个类有一个构造函数允许我们在定义流对象的时候以文件名初始化,如下:

ifstream fin("test.txt");

ifstream fin("test.txt",ios::in);

const string s = "test.txt";

ifstream fin(s,ios::in);

ofstream,ftream和上面是类似的,就不详细展开了

接下来说说几个重要的函数,

一个是成员函数is_open(),可以判断文件是否正确打开,如果是,返回true,否则,返回false。

然后是getline()函数,这个函数是按行读取txt中的内容,示例如下

ifstream fin("test.txt",ios::in);
string s;
while(getline(fin,s))
cout << s;//输出每一行

每次从fin指向的文件中读取一行,一行之中的所有字符都会被读入,包括空格。但是结尾的空格不读入,回车换行也不读入。

我们知道,所有输入/输出流对象(i/o streams objects)都有至少一个流指针:

  • ifstream, 类似istream, 有一个被称为get pointer的指针,指向下一个将被读取的元素。
  • ofstream, 类似 ostream, 有一个指针 put pointer ,指向写入下一个元素的位置。
  • fstream, 类似 iostream, 同时继承了get 和 put

我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针

tellg() 和 tellp()
这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前get 流指针的位置 (用tellg) 或 put 流指针的位置(用tellp).
seekg() 和seekp()
这对函数分别用来改变流指针get 和put的位置。两个函数都被重载为两种不同的原型:
seekg ( pos_type position );
seekp ( pos_type position );
使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数 tellg 和tellp 的返回值类型相同。
seekg ( off_type offset, seekdir direction );
seekp ( off_type offset, seekdir direction );
使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:

ios::beg 从流开始位置计算的位移
ios::cur 从流指针当前位置开始计算的位移
ios::end 从流末尾处开始计算的位移

文本中的字符是从position = 0开始的,例如

//假设test.txt中的内容是HelloWorld
ifstream fin("test.txt",ios::in);
cout << fin.tellg();//输出0,流置针指向文本中的第一个字符,类似于数组的下标0 char c;
fin >> c;
fin.tellg();//输出为1,因为上面把fin的第一个字符赋值给了c,同时指针就会向后 移动一个字节(注意是以一个字节为单位移动)指向第二个字符 fin.seekg(,ios::end);//输出10,注意最后一个字符d的下标是9,而ios::end指向的是最后一个字符的下一个位置 fin.seekg(,ios::beg);//和上面一样,也到达了尾后的位置 //我们发现利用这个可以算出文件的大小 int m,n;
m = fin.seekg(,ios::beg);
n = fin.seekg(,ios::end);
//那么n-m就是文件的所占的字节数 我们也可以从文件末尾出发,反向移动流指针,
fin.seekg(-,ios::end);//回到了第一个字符

tellp()以及seekp()的用法类似

顺便讲一下 << 以及 >> 这两个操作符

由于ifstream以及ofstream分别继承于istream和ostream,所以他们也分别继承了相应的运算符,这就是为什么我们可以有下面这种操作的原因了:

ifstream fin("test.txt");

char c;

fin >> c;

这其实和我们从控制台读取文件是一个道理,我们利用istream 中的对象cin 和其中的操作符 >> 来从控制台读取数据

这里我们用fin对象和 >> 操作符来从相应的文件中读取数据,相当于流的方向从控制台改变到文件中去了

不过这里应该要注意一下 >> 这个操作符的特点,有如下例子:

假如test.txt中的内容是Hello World

ifstream fin("test.txt");

string s;

fin >> s;

cout << s;

我们发现输出并不是Hello World,而是Hello,这是为什么呢?

几番试验后,我们发现,文件的每个空白之后, ">>" 操作符会停止读取内容, 直到遇到另一个>>操作符. 因为我们读取的每一行都被换行符分割开(是空白字符), ">>" 操作符只把这一行的内容读入变量。这就是这个代码也能正常工作的原因。如果你想把整行读入一个char数组, 我们没办法用">>"?操作符,因为每个单词之间的空格(空白字符)会中止文件的读取。为了验证,有如下示例:

char  c[20] ;

fin >> c;

我们想包含整个句子, 所以当我们想读取整行时,我们得用上面提到的getline()。这就是我们要做的:

fin.getline(c, 20);

cout << c;

这是函数参数. 第一个参数显然是用来接受的char数组. 第二个参数是在遇到换行符之前,数组允许接受的最大元素数量

现在我们得到了想要的结果:Hello World。

或者这样用也行:

string s;

getline(fin,s);

cout << s;

注意这样操作后流指针会指向文件的尾后

接下来说说本文的重点了,那就是以二进制的形式读写文件

这里的重点就是两个函数的使用,分别是read()和write()函数

二进制文件会复杂一点, 但还是很简单的。 首先你要注意我们不再使用插入和提取操作符(译者注:<< 和 >> 操作符). 你可以这么做,但它不会用二进制方式读写。你必须使用read() 和write() 方法读取和写入二进制文件. 创建一个二进制文件, 看下一行。

ofstream fout("file.dat", ios::binary);

这会以二进制方式打开文件, 而不是默认的ASCII模式。首先从写入文件开始。函数write() 有两个参数。 第一个是指向对象的char类型的指针, 第二个是对象的大小(译者注:字节数)。 为了说明,看例子:

int number = 30;

fout.write((char *)(&number), sizeof(number));

第一个参数写做"(char *)(&number)". 这是把一个整型变量转为char *指针。如果你不理解,可以立刻翻阅C++的书籍,如果有必要的话。

第二个参数写作"sizeof(number)". sizeof() 返回对象大小的字节数. 就是这样!

这样就写入了整个结构! 接下来是输入. 输入也很简单,因为read()函数的参数和 write()是完全一样的, 使用方法也相同。

ifstream fin("file.dat", ios::binary);

fin.read((char *)(&obj), sizeof(obj));

我不多解释用法, 因为它和write()是完全相同的。二进制文件比ASCII文件简单, 但有个缺点是无法用文本编辑器编辑。 接着, 我解释一下ifstream 和ofstream 对象的其他一些方法作为结束.

参考文章:

http://blog.csdn.net/kingstar158/article/details/6859379

http://www.cnblogs.com/greatverve/archive/2012/10/29/cpp-io-binary.html

http://blog.csdn.net/lightlater/article/details/6364931

C++ fstream 详解的更多相关文章

  1. C++文件读写详解(ofstream,ifstream,fstream)

    C++文件读写详解(ofstream,ifstream,fstream) 这里主要是讨论fstream的内容: #include <fstream> ofstream //文件写操作 内存 ...

  2. delphi 资源文件详解

    delphi资源文件详解 一.引子: 现在的Windows应用程序几乎都使用图标.图片.光标.声音等,我们称它们为资源(Resource).最简单的使用资源的办法是把这些资源的源文件打入软件包,以方便 ...

  3. MD5加密详解

    MD5加密详解 引言: 我在百度百科上查找到了关于MD5的介绍,我从中摘要一些重要信息: Message Digest Algorithm MD5(中文名为信息摘要算法第五版)为计算机安全领域广泛使用 ...

  4. C++通过jsoncpp类库读写JSON文件-json用法详解

    介绍: JSON 是常用的数据的一种格式,各个语言或多或少都会用的JSON格式. JSON是一个轻量级的数据定义格式,比起XML易学易用,而扩展功能不比XML差多少,用之进行数据交换是一个很好的选择. ...

  5. 18、标准IO库详解及实例

    标准IO库是由Dennis Ritchie于1975年左右编写的,它是Mike Lestbain写的可移植IO库的主要修改版本,2010年以后, 标准IO库几乎没有进行什么修改.标准IO库处理了很多细 ...

  6. 孙鑫视频VC++深入详解学习笔记

    孙鑫视频VC++深入详解学习笔记 VC++深入详解学习笔记 Lesson1: Windows程序运行原理及程序编写流程 Lesson2: 掌握C++基本语法 Lesson3: MFC框架程序剖析 Le ...

  7. sift代码实现详解

    1.创建高斯金字塔第-1组 1.1.将源图片转成灰度图 void ConvertToGray(const Mat& src, Mat& dst) { cv::Size size = s ...

  8. OpenCV-Mat结构详解

    前面博客中Mat函数谈到一些理解,但是理解的比较浅显,下面谈谈通道,行列等意义: Mat的常见属性 opencv中type类型· CV_<bit_depth>(S|U|F)C<num ...

  9. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

随机推荐

  1. p1144一元三次方程求解

    题目描述: 有形如:f(x)=ax^3+bx^2+cx+d=0这样的一元三次方程,给出该方程中各项的系数a,b,c,d,它们均为实数,并约定该方程一定存在着3个不同的实数解,解的范围在-100至100 ...

  2. Eclipse通过jdbc连接数据库制作简单登陆界面

    一.前言: 做网站开发,要求有多种搭配方式,前台技术可以使用PHP.ASP.JSP.ASP.NET.CGI等任何一种: 需要用到的基础语言用的最多的就是HTML/CSS.JS.JAVA.XML这些了, ...

  3. 每天一个linux命令(55)--at命令

    在Windows系统中,Windows提供了计划任务这一功能,在控制面板  ->  性能与维护  ->  任务计划,它的功能就是安排自动运行的任务.通过 ‘ 添加任务计划’ 的一步步引导, ...

  4. C# 的四舍五入

    c#的四舍五入有两种情况: 1.常规四舍五入 (decimal).ToString("f2") 2.四舍六入五取偶 除1里面的其他方式四舍五入都是四舍六入五取偶.

  5. 【SysML】用例图

    引言 对于系统工程师来说,设计用例图是一种极为常见的建模活动.用例图是一种黑盒视图,通过向读者传递一系列的用例以及相关的参与者,对系统对外提供的服务或系统具备的行为进行建模.在详细讨论SysML的用例 ...

  6. smarty模板做人员表信息删除,修改 里面的性别单选按钮民族下拉,另外登录进去可以显示姓名

    首先登录进去可以显示姓名 smarty模板做人员表信息删除,删除的时候有提示框确定删除吗. 修改 里面的性别单选按钮,要修改谁有默认选中,用了变量调节器 民族位置做下拉,用<{foreach}& ...

  7. java开发之阿里云对象存储OSS和云数据库Memcache的使用

    web开发中标配:aliyun ECS(阿里云服务器),aliyun RDS(阿里云数据库),aliyun OSS(阿里云对象存储),aliyun Memcache(阿里云缓存数据库). 今天就介绍下 ...

  8. 《Django By Example》第九章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者@ucag 注:哈哈哈,第九章终于来啦 ...

  9. Mutillidae在kali linux上的安装

    XAMPP:下载地址(https://www.apachefriends.org/download.html) Mutillidae:下载地址(http://sourceforge.net/proje ...

  10. ps-图像的符合

    1.将所需要的背景和素材添加到同一个画布中 2.选择素材图层---工具栏---修复画笔工具-----alt+左键,在素材上进行定位 3.切换到背景图层 4.按住鼠标左键并在合适位置进行拖动, 5.松开 ...