ITKeyword,专注技术干货聚合推荐

注册 | 登录

C++笔记,        C++标准IO库----基本点概括

jiangxinyu 分享于 2012-07-18

推荐:C++:C++Primer读书笔记(6)--标准IO库

from : http://blog.163.com/thomaskjh@126/blog/static/37082998201192594238842/ (1)       IO对象不能复制或赋值,因此形参或返回类型不能为流类型,可以是

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
地址https://www.aliyun.com/minisite/goods

多线程编程之一——问题提出(http://www.vckbase.com/index.php/wv/1412)

多线程编程之二——MFC中的多线程开发(http://www.vckbase.com/index.php/wv/1414)

多线程编程之三——线程间通讯(http://www.vckbase.com/index.php/wv/1415)

多线程编程之四——线程的同步(http://www.vckbase.com/index.php/wv/1416)

多线程管理类(http://www.vckbase.com/index.php/wv/1502)

多线程在C、Win32和MFC下的使用方法(http://www.vckbase.com/index.php/wv/1660)

VC知识库(http://www.vckbase.com/)

 

C++标准库简介

C++标准库的所有头文件都没有扩展名。C++标准库的内容总共在50个标准头文件中定义,其中18个提供了C库的功能。<cname>形式的标准头文件【 <complex>例外】其内容与ISO标准C包含的name.h头文件相同,但容纳了C++扩展的功能。在 <cname>形式标准的头文件中,与宏相关的名称在全局作用域中定义,其他名称在std命名空间中声明。在C++中还可以使用name.h形式的标准C库头文件名。

C++标准库的内容分为10类:

C1.语言支持 C2.输入/输出 C3.诊断 C4.一般工具 C5.字符串

C6.容器 C7.迭代器支持 C8.算法 C9.数值操作 C10.本地化


C1 标准库中与语言支持功能相关的头文件 头文件 描述
<cstddef> 定义宏NULL和offsetof,以及其他标准类型size_t和ptrdiff_t。与对应的标准C头文件的区别是,NULL是C++空指针常量的补充定义,宏offsetof接受结构或者联合类型参数,只要他们没有成员指针类型的非静态成员即可。
<limits> 提供与基本数据类型相关的定义。例如,对于每个数值数据类型,它定义了可以表示出来的最大值和最小值以及二进制数字的位数。
<climits> 提供与基本整数数据类型相关的C样式定义。这些信息的C++样式定义在 <limits>中
<cfloat> 提供与基本浮点型数据类型相关的C样式定义。这些信息的C++样式定义在 <limits>中
<cstdlib> 提供支持程序启动和终止的宏和函数。这个头文件还声明了许多其他杂项函数,例如搜索和排序函数,从字符串转换为数值等函数。它与对应的标准C头文件stdlib.h不同,定义了abort(void)。abort()函数还有额外的功能,它不为静态或自动对象调用析构函数,也不调用传给atexit()函数的函数。它还定义了exit()函数的额外功能,可以释放静态对象,以注册的逆序调用用atexit()注册的函数。清除并关闭所有打开的C流,把控制权返回给主机环境。
<new> 支持动态内存分配
<typeinfo> 支持变量在运行期间的类型标识
<exception> 支持异常处理,这是处理程序中可能发生的错误的一种方式
<cstdarg> 支持接受数量可变的参数的函数。即在调用函数时,可以给函数传送数量不等的数据项。它定义了宏va_arg、va_end、va_start以及va_list类型
<csetjmp> 为C样式的非本地跳跃提供函数。这些函数在C++中不常用
<csignal> 为中断处理提供C样式支持

C2 支持流输入/输出的头文件 头文件 描述
< iostream> 支持标准流cin、cout、cerr和clog的输入和输出,它还支持多字节字符标准流wcin、wcout、wcerr和wclog。
<iomanip> 提供操纵程序,允许改变流的状态,从而改变输出的格式。
<ios> 定义iostream的基类
<istream> 为管理输出流缓存区的输入定义模板类
<ostream> 为管理输出流缓存区的输出定义模板类
<sstream> 支持字符串的流输入输出
<fstream> 支持文件的流输入输出
<iosfwd> 为输入输出对象提供向前的声明
<streambuf> 支持流输入和输出的缓存
<cstdio> 为标准流提供C样式的输入和输出
<cwchar> 支持多字节字符的C样式输入输出

C3 与诊断功能相关的头文件 头文件 描述
<stdexcept> 定义标准异常。异常是处理错误的方式
<cassert> 定义断言宏,用于检查运行期间的情形
<cerrno> 支持C样式的错误信息

C4 定义工具函数的头文件 头文件 描述
<utility> 定义重载的关系运算符,简化关系运算符的写入,它还定义了pair类型,该类型是一种模板类型,可以存储一对值。这些功能在库的其他地方使用
<functional> 定义了许多函数对象类型和支持函数对象的功能,函数对象是支持operator()()函数调用运算符的任意对象
<memory> 给容器、管理内存的函数和auto_ptr模板类定义标准内存分配器
<ctime> 支持系统时钟函数

C5 支持字符串处理的头文件 头文件 描述
<string> 为字符串类型提供支持和定义,包括单字节字符串(由char组成)的string和多字节字符串(由wchar_t组成)
<cctype> 单字节字符类别
<cwctype> 多字节字符类别
<cstring> 为处理非空字节序列和内存块提供函数。这不同于对应的标准C库头文件,几个C样式字符串的一般C库函数被返回值为const和非const的函数对替代了
<cwchar> 为处理、执行I/O和转换多字节字符序列提供函数,这不同于对应的标准C库头文件,几个多字节C样式字符串操作的一般C库函数被返回值为const和非const的函数对替代了。
<cstdlib> 为把单字节字符串转换为数值、在多字节字符和多字节字符串之间转换提供函数

C6 定义容器类的模板的头文件 头文件 描述
<vector> 定义vector序列模板,这是一个大小可以重新设置的数组类型,比普通数组更安全、更灵活
<list> 定义list序列模板,这是一个序列的链表,常常在任意位置插入和删除元素
<deque> 定义deque序列模板,支持在开始和结尾的高效插入和删除操作
<queue> 为队列(先进先出)数据结构定义序列适配器queue和priority_queue
< stack> 为堆栈(后进先出)数据结构定义序列适配器stack
< map> map是一个关联容器类型,允许根据键值是唯一的,且按照升序存储。multimap类似于map,但键不是唯一的。
<set> set是一个关联容器类型,用于以升序方式存储唯一值。multiset类似于set,但是值不必是唯一的。
<bitset> 为固定长度的位序列定义bitset模板,它可以看作固定长度的紧凑型bool数组

C7 支持迭代器的头文件 头文件 描述
<iterator> 给迭代器提供定义和支持

C8 有关算法的头文件 头文件 描述
<algorithm> 提供一组基于算法的函数,包括置换、排序、合并和搜索
<cstdlib> 声明C标准库函数bsearch()和qsort(),进行搜索和排序
<ciso646> 允许在代码中使用and代替&&

C9 有关数值操作的头文件 头文件 描述
<complex> 支持复杂数值的定义和操作
<valarray> 支持数值矢量的操作
<numeric> 在数值序列上定义一组一般数学操作,例如accumulate和inner_product
< cmath> 这是C数学库,其中还附加了重载函数,以支持C++约定
<cstdlib> 提供的函数可以提取整数的绝对值,对整数进行取余数操作

C10 有关本地化的头文件 头文件 描述
<locale> 提供的本地化包括字符类别、排序序列以及货币和日期表示。
<clocale> 对本地化提供C样式支持

 

C++ std命名空间

1. 什么是命名空间

在编程语言中,命名空间是一种特殊的作用域,它包含了处于该作用域中的所有标示符,而且其本身也是由标示符表示的。命名空间的使用目的是为了将逻辑相关的标示符限定在一起,组成相应的命名空间,可使整个系统更加模块化,最重要的是它可以防止命名冲突。就好比在两个函数或类中定义相同名字的对象一样,利用作用域标示符限定该对象是哪个类里定义的。

2. C++中的命名空间定义

C++语言中,命名空间使用namespace来声明,并使用{ }来界定命名空间的作用域,例如:

namespace foo{

int num=0;

}

3. C++中的std命名空间

std命名空间是C++中标准库类型对象的命名空间。

在标准C++以前,都是用#include<iostream.h>这样的写法的,因为要包含进来的头文件名就是iostream.h。标准C++引入了名字空间的概念,并把iostream等标准库中的东东封装到了std名字空间中,同时为了不与原来的头文件混淆,规定标准C++使用一套新的头文件,这套头文件的文件名后不加.h扩展名,如iostream、string等等,并且把原来C标准库的头文件也重新命名,如原来的string.h 就改成cstring(就是把.h去掉,前面加上字母c),所以头文件包含的写法也就变成了#include <iostream>

并不是写了#include<iostream>就必须用using namespace std;我们通常这样的写的原因是为了一下子把std名字空间的东东全部暴露到全局域中(就像是直接包含了iostream.h这种没有名字空间的头文件一样),使标准C++库用起来与传统的iostream.h一样方便,但并不建议这样做,因为使用using namespace std;的话就没有起到命名空间的作用。再次回到了如同没有涉及命名空间时,所有标示符都定义在全局作用于中的混乱情况,不利于程序员创建新对象。

如果不用using namespace std;使用标准库时就得时时带上名字空间的全名,如std::cout << "hello" << std::endl;

<iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。

如下写法,则出错

#include <iostream.h>

using namespace std;

所以 要么写成
#include <iostream>

using std::cout;

using std::cin;

using std::cerro;

要么写成

#include <iostream.h>

当然最好是前种,因为后种情况如果遇到用户定义的标示符在头文件中已经定义,可能会导致错误,采用using namespace std也是如此。

4. 命名空间实际上是一个作用域

例如:
在x.h中的内容为

// x.h
namespace MyNamespace1
{
   class MyClass
   {
   public:
       void f();
   private:
       int m;
   }
};

在y.h中的内容为

// y.h
namespace MyNamespace2
{
   class MyClass
   {
   public:
       void f();
   private:
       int m;
   }
};

然后在z.cpp中引入x.h和y.h

// z.cpp
#include "x.h"   
#include "y.h" 

void z::f()
{
   //声明一个文件x.h中类MyClass的实例x
   MyNamespace1::MyClass x;
     //声明一个文件x.h中类MyClass的实例x
   MyNamespace2::MyClass y;

   //调用文件x.h中的函数f
   x.f();
   //调用文件y.h中的函数f
   y.f();
}

5. 使用标准库中标示符的方法

  • 使用标示符限定命名空间:std::cout<<"Hello!"<<endl;
  • 使用using std::cout;事先声明:cout<<"Hello!"<<endl;   //分别引入,需要用哪个引用 哪个,保证程序中名称的唯一性
  • 使用using naspace std声明:cout<<"Hello!"<<endl;    //引入名字空间的所有内容,不推荐这样写

因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。所以就有了<iostream.h>和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。命名空间std封装的是标准库的名称,标准库为了和以前的头文件区别,一般不加".h"

C++语言是从C语言发展起来的,因此有很多借鉴的地方。当C++语言推出但尚未标准化以前(98年才标准化),市场上已经有了很多版本的程序库了,各库林立,导致互相应用时出现了一个很难调和的难题,那就是命名冲突,又称名空间泛滥。比如某个库写了个函数line(int x,int y);不巧另外一个库又写了个类class line;这下编译器该匹配哪个呢?只好取决于哪个库文件先被引用到文件中,并且把另外一个完全屏蔽掉。这显然不是一个好的方法。在标准库的产生过程中,这个问题被提了出来。为此,标准库组织决定在标准库中引入名空间的概念,所有标准库的组件都在名空间std中定义,由用户手动引入到程序中,这样就让编译器知道,当遇到一个可能冲突的名称时,以标准库中定义的名称为主,如果想用标准库外定义的名称,那程序员需要自己注明另外的名空间,从而达到消除名空间泛滥的目的。using namespace std;的写法引入了名空间的所有内容,这是一种简单但不保险的做法,是标准库组织不推荐这么做的。因为这样引入了所有的组件函数名,相当于重新引发了名字空间泛滥的问题。为此,好的做法应该是第二种分别引入。

 

C++学习----编程规范

1. 命名规范

1.1 一般命名规范

  • C++命名规范包括 文件命名规范和 标识符命名规范
  • 标识是指C++中语法对象的名字(常量名、变量名、函数名、类和类别名以及预处理名等),其基本语法是以字母开头,由字母数字和下划线组成。
  • 标识符最好由两部分组成:标识前缀+含义标识。
  • 标识前缀最好用不超过三个字母的缩写词组成,缩写最好全部大写。
  • 含义标识由一个或多个单词组成时,一般不缩写,除非此缩写很常用。

1.2 文件命名规范

  • 文件名必须由前缀和后缀组成,最好采用长文件命名格式。
  • 文件名必须用英文字母表达,避免使用中文。
  • 对于机构单词组合表达的文件名,每个单词第一个字母必须大写。
  • 源文件后缀格式可以为.cpp,头文件后缀格式可以为.h。
  • 每一个类最好都有一个头文件和源文件,头文件和源文件都必须与类名相对应。

例如:类CBufferControl对应BufferControl.h,BufferControl.cpp。

1.3 变量命名规范

  • 变量命名格式必须是:scope_+prefix_+qualifer(范围前缀+类型前缀+含义标识)。
  • 范围前缀的取名遵照下表:

  • 当变量作为函数参数时,建议如下使用范围前缀:

i_:输入参数

o_:输出参数

io_:输入输出参数

1.4 函数命名规范

  • 类成员函数命名规则最好是:函数的含义标识。
  • 其它函数命名规则最好是:范围前缀_+函数返回置类型+函数的含义标识。
  • 函数的含义标识必须 能反映函数实现的功能。
  • 函数的含义标识中第 一个字母必须大写。
  • 对于几个单词组合表达的函数的含义标识, 每个单词第一个字母必须大写。
  • 组合单词时最好采用动宾结构。如CheckErrors()。

1.5 类命名规范

  • 类第一个字母必须为大写“C”。
  • 对于几个单词组合表达的类名,每一个单词第一个字母需大写。

1.6 其他命名规范

  • 常量中所有字母必须大写,单词间用“_”隔开。例如:const int A_GLOBAL_CONSTANT=4;
  • 联合体类型命名的格式为“UN_含义标识”,含义标识全部大写,单词间用“_”隔开。
  • 结构体类型命名的格式为“ST_含义标识”,含义标识全部大写,单词间用“_”隔开。
  • 枚举类型命名的格式为“EN_含义标识”,含义标识全部大写,单词间用“_”隔开。
  • 宏,所有字母必须大写,单词间用“_”隔开。

2. 注释规范

2.1 一般规范

  • 注释的目的是解释代码的目的、功能和算法,提供代码以外的信息,帮助读者理解代码,禁止注释无关信息。
  • 注释必须 语言简练,明确易懂。
  • 单行注释必须用双斜杠进行注释,多行注释必须用/**/, 注释内容最好放在对应代码的上方。
  • 注释与前面的执行语句之间必须空一行。
  • 必须 一边写代码一边注释,修改代码同时修改相应的注释,以保证 注释与代码的一致性。
  • 注释行数(不包括程序头和函数头说明部分)必须占总行数的1/5到1/3。

2.2 常量、变量和宏的注释

  • 被保存值的定义(必选)
  • 合法取值的范围(可选)
  • 与其他数据,函数或模块的关系(可选)

例如:

//default quantilizer level, from 1 to 31.

const int DEFAULT_QL=8;

//number of gob, 12 for CIF and 3 for QCIF.

int nGob;

2.3 结构体和联合体的注释

  • 他描述的对象是什么(必须)
  • 对其分量应按变量注释要求加以注释(必须)
  • 与其他数据,函数或模块的关系(可选)

例如:

//3D Point(nPosX, nPosY, nPosZ)

typedefstruct ST_ THREED_ PT_TAG{

int nPosX; //x Position

int nPosY; //y Position

intnPosZ; //z Position

}ST_ THREED_ PT;

2.4 函数的注释

  • 对于比较重要的函数或方法必须在其 声明处作适当注释,说明该函数的功能及参数的含义,注释模板如下:

//Get data pointer of sub image

Short* GetSubImageData(int i_nLeft, //Left position of sub image.

                                          inti_nTop, //Top position of sub image.

                                          WORD i_wWidth, //Width of sub image.

                                          WORD i_wHeight); //Height of sub image.

  • 对于自行编写的函数,若是系统关键函数,必须在 函数实现部分的上方标明该函数的信息,格式如下:

/****************************************************

* 函数名:

* 功能描述:

* 返回值:

* 参数:(名称,含义,取值说明)

* 作者:

* 时间:

* 修改记录:(修改序号,日期,修改人员,修改说明)(在修改记录栏必须按时间先后顺序自下往上排列。)

*****************************************************/

/****************************************************

* Function Name:

* Description:

* Return Value:

* Parameters :(Name,Description,Value)

* Author:

* Date:

* Change log:(ID,Date,Author,Description)

*****************************************************/

注意:注释文本每行的跨度禁止超过上下条纹框的范围,换行时必须以冒号所在位置为准进行左对齐。

2.5 文件头部注释

  • 在 文件头部必须加注释表明该文件的一些信息,其格式如下:

/****************************************************

* 文件名 :

* 版本 : 如:1.0/0.8/0.7

* 功能描述 :

* 创建日期 :

* 作者 :

* 修改记录:(序号,修改说明,日期,修改人员)(在修改记录栏必须按时间先后顺序自下往上排列,修改记录中必须特别注明修改了哪些函数)

*****************************************************/

/****************************************************

推荐:C++ Primer学习系列(3):函数/标准IO库/顺序容器

第 7 章 函数 7.1 函数调用操作符 ():操作数是函数名和一组(可能为空)由逗号分隔的实参 实参必须具有与形参类型相同、或者能隐式转换为形参类型的数据类型 函

* File Name :

* Version Number :

* Description :

* Date: * Author:

* Change log:(ID,Date,Author,Description)

*****************************************************/

  • 注意:注释文本每行的跨度禁止超过上下条纹框的范围,换行时必须以冒号所在位置为准进行左对齐。

2.6 语句注释

  • 对语句的注释必须放在其 上方相邻位置,不要放在下面。
  • 在 程序块的结束行右方最好加注释标记,以表明某程序块的结束。
  • 过长的函数实现,最好将其语句 按实现的功能分段加以概括性说明。
  • 必须对 不易理解的分支条件表达式加注释

 

        C++标准IO库----基本点概括

 

概述

先不要急着知道怎么用这个玩意,让我们一起先来看一看C++标准IO库的框架,其实挺有意思的!那就开始吧!

C++的输入输出由标准库提供,标准库提供了一族类型,支持对文件、string对象、和控制窗口等设备的读写。一方面,这些IO类型都定义了如何读写内置类型的的值,另一方面,用户在设计类时可以仿照IO标准库设施读写内置类型的方式设计自己的输入输出操作。

1. 面向对象的IO库

面向对象是C++的一大特色,他的标准库自然也不例外,统统都是面向对象设计的。标准库使用了继承(inheritance)来定义一组面向对象(object-oriented)类。IO类型定义在三个独立的头文件中:iostream定义读写控制窗口的类型;fstream定义读写已命名文件的类型;sstream定义读写存储在内存中的string对象的类型。各头文件包含的类型及其含义以及各类型之间的继承关系如下图:

注意:

  • 以上所讲的流类型(stream class)读写的是由char类型组成的流。除此之外,标准库还定义了一组相关的类型,支持wchar_t类型。即,每个类名前都加上“w”前缀,以此与char类型的版本区别开来。
  • IO对象不允许复制和赋值:因此流对象不可以存储在vector或其他容器里;函数形参和返回类型也不可以是流类型。

2. 标准IO库定义的------条件状态

标准IO 库管理一系列条件状态(condition state)成员,用来标记给定的 IO 对象是否处于可用状态,或者碰到了哪种特定的错误。如下列出了标准库定义的一组函数和标记,提供访问和操纵流状态的手段。

strm::iostate               这是机器相关的整型别名,代表一种类型,由各个io类定义,用于定义条件状态(strm在这里代表各个流类型)。

strm::badbit                strm::iostate类型的值,用于指出被破坏的流。

strm::failbit                 strm::iostate类型的值,用于指出失败的 IO 操作。

strm::eofbit                 strm::iostate类型的值,用于指出流已经到达文件结束符。

s.eof()                        返回eof标志位的值,真或假。

s.fail()                        返回fail标志位的值,真或假。

s.bad()                      返回bad标志位的值,真或假。

s.good()                    检测流s的有效性,当eof,fail,bad都不为真时good标志位有效,调用good()函数返回真值。

s.clear()                    将流s中的所有状态值都重设为有效状态。

s.clear(flag)               将流s中的某个指定条件状态置为有效,flag是strm::iostate类型的条件状态,若括号内不写具体条件状态则将所有条件状态全部设置有效。

s.setstate(flag)          给流添加指定条件,设置flag位为触发状态,flag是strm::iostate类型对象。

s.rdstate()                 返回流s的当前条件,返回值类型为strm::iostate。

总结-----条件状态:

  • 所有流对象都包含一个条件状态成员,他是strm::iostate类型的对象,以二进制位的形式使用。
  • failbit, eofbit, badbit和goodbit是四个strm::iostate类型的常量值,他们本身分别表示一种条件状态,具体如下表:

常量 含义 fail标志位 oef标志位 bad标志位 ios::badbit 已达到文件结束 0 0 1 ios::eofbit 输入(输出)流出现非致命错误,可挽回 0 1 0 ios::failbit 输入(输出)流出现致命错误,不可挽回 1 0 0 ios::goodbit 流状态完全正常 0 0 0

  • badbit, eofbit, failbit,goodbit构成了四个基本流状态。
  • 用cout检测goodbit, badbit, eofbit, failbit的值分别是0,1,2,4,这与上面的表格不是正好完全对应着吗,呵呵(goodbit:0000 0000;  badbit:0000 0001;eofbit:0000 0010; failbit:0000 0100)。
  • 可使用clear和setstate来操作和管理条件状态成员,他们常与badbit,eofbit,failbit,goodbit以及位或“|”结合起来使用。
  • 用bad(), fail(), eof()和good()操作可以检测流状态是否属于,还可以用rdstate()来返回整个条件状态成员。

2.1 rdstate的使用:

rdstate成员函数返回一个iostate类型的值,前面已经说过,这是一个与机器相关的整型,该值对应于流的整个条件状态。如下代码:(摘自cplusplus.com)

// getting state of stream object

#include <iostream>

#include <fstream>

using namespace std;

int main ()

ifstream is; 

is.open ("test.txt");

if ( (is.rdstate() & ifstream::failbit ) != 0 ) //检测fail标志位是否被设置,即是否遇到错误  

cerr << "Error opening 'test.txt'\n"; return 0;

}

2.2 clear的使用:

clear用于设置标志位而不是清除标志位,即按照实参设置流状态,强制覆盖原有状态,其工作方式有两种:

  • 带有failbit, eofbit, badbit,goodbit中的一个或多个作为参数,按照具体实参设置流对象,例如goodbit作实参时就设置流状态各位为0。
  • 不带参数,即默认状态,效果如同带goodbit作为参数,会将所有标志位设置为有效。

// clearing errors
#include <iostream>
#include <fstream>
using namespace std;

int main () {
  char buffer [80];
  fstream myfile;

  myfile.open ("test.txt",fstream::in);

  myfile << "test";
  if (myfile.fail())
  {
    cout << "Error writing to test.txt\n";
    myfile.clear();   }

  myfile.getline (buffer,80);
  cout << buffer << " successfully read from file.\n";

  return 0;
}

2.3 setstate的使用:

setstate用于设置标志位状态,与clear的区别在于它只是在原有状态的基础上以叠加的方式更新实参对应状态标志,而不会强制覆盖原有状态,使用方式:

  • 传递一个strm::iosatate类型的参数(可以是单个的,也可以是用“位或”连接的多个iostate对象)

3. 输出缓冲区的管理-----关键在于控制如何刷新缓冲区

每一个IO对象管理一个缓冲区,用于存储程序读写的数据。缓冲区的刷新会使其中的内容写入到真实的输出设备或文件中。以下情况都会导致缓冲区被刷新:

  • 程序正常结速时,将清空所有输出缓冲区。
  • 在一些不确定的时候,缓冲区可能已经满了,这种情况下,缓冲区将会在写入下一个值之前刷新缓冲区。
  • 用操纵符(endl, ends,flush)显式地刷新缓冲区:

cout<<"Hi!"<<endl; //插入换行符,并刷新缓冲区

cout<<"Hi!"<<ends; //插入空格字符null,并刷新缓冲区

cout<<"Hi!"<<flush; //仅仅刷新缓冲区,不插入任何其他字符

  • 用unitbuf操纵符设置流的内部状态,使得流在每次输出操作执行完都清空缓冲区,恢复系统默认管理方式用nounitbuf。

cout<<unitbuf<<"first"<<"second"<<"third"<<nounitbuf; //等价于cout<<"first"<<flush<<"second"<<flush<<"third"<<flush;

  • 使用tie函数将输出流与输入流关联起来,当输入流与输出流绑定在一起的时候,任何输入操作都将首先刷新其输出流关联的缓冲区。

补充------------tie函数的使用:

tie函数可以由istream或ostream对象调用,使用一个ostream流对象的指针做形参。

  • cin.tie(&cout);//将cin和cout绑定在一起,cin>>ival;时将导致cout所关联的缓冲区被刷新。
  • ostream *old_tie=cin.tie(); //不传递参数时,返回当前绑定的对象
  • cin.tie(0); 接受0作为参数时,解除当前绑定

本文只作为C++标准IO库的基础概括,文件、string对象、和控制窗口的具体IO操作方法,将另外撰文,链接如下:

 

 

C++中的文件输入输出 

C++中的文件输入输出:http://blog.csdn.net/miss_acha/article/details/7194695

 

简介 本教程将以C++最基本的文件I/O(输出/输出)开始。此后,我将从更深入的方面,为你展示一些技巧,并分析给出一些有用的函数。 你需要对C++有一个较好的理解,否则这个教程于你而言将是陌生而毫无用处。 你的第一个程序 首先我将给出一段代码,接着再逐行进行解释。我们的第一个程序将建立一个文件,并写入一些字符: #include <fstream.h> void main() // 程序从这里开始运行
{ ofstream SaveFile(“cpp-home.txt”); SaveFile << “Hello World, from www.cpp-home.com and Loobian!”; SaveFile.close();
} 仅仅如此吗?没错!这个程序将在当前运行目录下建立一个名为cpp-home.txt的文件,并向它写入“Hello World, from www.cpp-home.com and Loobian!”。 下面给出各行的含义: #include <fstream.h> —— 你需要包含此文件以使用C++的文件输入/输出函数。注意:一旦包含了这个文件,你不再需要(为了使用cout/cin)包含iostream.h,因为fstream.h已经自动包含了它。 在这个头文件中声明了若干个类,包括ifstream,ofstream及fstream,它们都继承自istream和ostream类。 ofstream SaveFile(“cpp-home.txt”); 1)ofstream即“output file stream(输出文件流)”。它将建立一个句柄(handle),以便我们以后能以一个文件流的形式写入文件。 2)SaveFile—— 这是文件句柄的名字,当然,你还可以换用任何一个你想要的名称。 3)(“cpp-home.txt”); —— 打开名为cpp-home.txt的文件。如果程序运行的当前目录已经存在这样一个文件,则它将被替换掉;万一不存在,程序也会为你创建一个为文件,你不必为此而担心。 现在,让我们稍微深入一点点。首先,我要指出的是:ofstream是一个类。因此ofstream SaveFile(“cpp-home.txt”);这一语句将创建一个该类的对象;而我们在括号中所传递的参数实际上将传给构造函数:在这里我们将我们要建立的文件的名称作为实际参数传递给了该类的构造函数。当然,我们还可以传递其它的一些信息,不过我以后再对其进行讲解。 SaveFile << “Hello World, from www.cpp-home.com and Loobian!”; —— “<<”看起来是不是很亲切?不错,想必你已经在cout << 中见到过。这是一个预定义好的运算符。不管怎么说,这行语句所做的,是将上面的那段文本写入文件。正如前面所提到的,SaveFile是一个文件句柄,它关联一个打开的流式文件。所以,我们只须输入句柄名,再跟着输入“<<”,然后接着写下一串用引号括起来的文本,就可以实现对文件的写入。如果我们想写入的是某个变量的值而不是带引号的文本,也只须像通常使用cout << 一样将变量传递给句柄对象,像这样: SaveFile << variablename; 就可以了! SaveFile.close(); —— 既然我们打开了一个流文件,那么当我们用完它之后,就必须关闭它。SaveFile是ofstream类的一个对象,而该类(ofstream)有一个用于关闭文件的成员函数,即close() 函数。因此,我们只要依次输入文件句柄名,点号和close(),就可以关闭该文件! 注意:一旦你关闭文件,在你重新打开它以前,就再不能对它进行访问。 以上就是一个可以写文件的最简单程序。的确很容易!不过,正如你即将在以后部分的教程中所看到的,还有更多的东西要学呢! 读取文件 你已经看到了应该如何写文件。现在,当我们已经得到cpp-home.txt文件时,我们将要读取它,并且将内容打印在屏幕上。 首先,我要指出的是,有很多种方法可以读取文件。以后我会向你们介绍所有的方法(就我所知的)。此刻,我先向你展示最佳的方法(我认为的)。 正如你已经熟悉的——我将首先给出一段程序代码,然后,我会详细地对它进行解释说明: #include <fstream.h> void main() // 程序从这里开始 {     ifstream OpenFile("cpp-home.txt");     char ch;     while(!OpenFile.eof())     {        OpenFile.get(ch);        cout << ch;     }     OpenFile.close(); } 你想必已经了解首行的意义所在,而剩下的部分将由我为你解释。 ifstream OpenFile(“cpp-home.txt”) —— 我猜它对现在的你而言多少会熟悉些!ifstream表示“input file stream(输入文件流)”。在前一节的程序中,出现的则是ofstream,它的意义是“output file stream(输出文件流)”。前一节的程序是进行文件的写操作,这就是它用“output(输出)”来表示的原因。而本节的程序则是读取一个文件,这就是它用“input(输入)”来表示的原因。文件流的输入输出均是站在缓存的角度称呼,例如ifstream是从文件读取内容到缓存,ofstream是从缓存读取写入文件。这一行剩下的代码于你而言应当是熟悉的了:OpenFile是ifstream类的一个对象,它将关联一个输入文件流;而用引号括住的内容,就是将要打开的文件的名称。 请注意:这里没有对要打开的文件是否存在进行测试!以后我将向你指出如何进行检测。 char ch; —— 声明一个字符数组(array of typechar)。只是有一点要提醒你:这样的数组(arrays)只能存储一个ASCII字符。 while(!OpenFile.eof()) —— 如果已经到达文件末尾,eof( )函数将返回一个非零值。因此我们所设计的这个循环将一直持续,直至我们的文件操作到达文件末尾。这样我们就可以遍历整个文件,以便对它进行读取。 OpenFile.get(ch); ——OpenFile是类ifstream的一个对象。该类声明了一个名为get( )的成员函数。只要我们拥有该对象,我们自然就可以调用这个函数。get( )函数从相应的流文件中读出一个字符,并将其返回给变量。在本例中,get( )函数只带一个参数——用于存储所读取的字符的变量。所以,调用OpenFile.get(ch)后程序将会从OpenFile流中读取一个字符并存入变量ch中。 注意:如果你再次调用该函数,它将读取下一个字符,而不是原来的那一个!你过后将理解为什么会这样。 这就是我们要不断反复循环直至读操作到达文件尾的原因。每循环一次,我们将读出一个字符并将它保存在ch中。 cout << ch; —— 显示ch变量值,它保存了读取得到的字符。 File.close(); —— 我们打开了一个流式文件,就需要关闭它。使用close()函数即可将它关闭,这和前一节的一样! 注意:一旦你关闭了一个文件,在你重新打开它之前,你不能再对它进行访问。 大功告成了!我希望你能明白我的解释。当你编译并运行这个程序的时候,它应当会输出: “Hello World, fromwww.cpp-home.com and Loobian!”
掌握输入/输出流 在这一章里,我会提及一些有用的函数。我将为你演示如何打开一个可以同时进行读、写操作的文件;此外,我还将为你介绍其它打开文件的方法,以及如何判断打开操作是否成功。因此,请接着往下读! 到目前为止,我已为你所展示的只是单一的打开文件的途径:要么为读取而打开,要么为写入而打开。但文件还可以以其它方式打开。迄今,你应当已经认识了下面的方法: ifstream OpenFile(“cpp-home.txt”); 噢,这可不是唯一的方法!正如以前所提到的,以上的代码创建一个类ifstream的对象,并将文件的名字传递给它的构造函数。但实际上,还存在有不少的重载的构造函数,它们可以接受不止一个的参数。同时,还有一个open()函数可以做同样的事情。下面是一个以上代码的示例,但它使用了open()函数: ifstream OpenFile; OpenFile.open(“cpp-home.txt”); 你会问:它们之间有什么区别吗?哦,我曾做了不少测试,结论是没有区别!只不过如果你要创建一个文件句柄但不想立刻给它指定一个文件名,那么你可以使用open()函数过后进行指定。顺便再给出一个要使用open()函数的例子:如果你打开一个文件,然后关闭了它,又打算用同一个文件句柄打开另一个文件,这样一来,你将需要使用open()函数。 考虑以下的代码示例: #include <fstream.h> void read(ifstream &T)//pass the file stream to the function {  //the method to read a file, that I showed you before     char ch;     while(!T.eof())     {        T.get(ch);        cout << ch;     }        cout << endl << "--------" << endl; } void main() {     ifstream T("file1.txt");     read(T);     T.close();     T.open("file2.txt");     read(T);     T.close(); } 据此,只要file1.txt和file2.txt并存储了文本内容,你将看到这些内容。 现在,该向你演示的是,文件名并不是你唯一可以向open()函数或者构造函数(其实都一样)传递的参数。下面是一个函数原型: ifstream OpenFile(char *filename, int open_mode); 你应当知道filename表示文件的名称(一个字符串),而新出现的则是open_mode(打开模式)。open_mode的值用来定义以怎样的方式打开文件。下面是打开模式的列表: 名称 描述 ios::in 打开一个可读取文件 ios::out 打开一个可写入文件 ios::app 你写入的所有数据将被追加到文件的末尾,此方式使用ios::out ios::ate 你写入的所有数据将被追加到文件的末尾,此方式不使用ios::out ios::trunk 删除文件原来已存在的内容(清空文件) ios::nocreate 如果要打开的文件并不存在,那么以此参数调用open()函数将无法进行。 ios::noreplace 如果要打开的文件已存在,试图用open()函数打开时将返回一个错误。 ios::binary 以二进制的形式打开一个文件。 实际上,以上的值都属于一个枚举类型的int常量。但为了让你的编程生涯不至于太痛苦,你可以像上表所见的那样使用那些名称。 下面是一个关于如何使用打开模式的例子: #include <fstream.h> void main() {     ofstream SaveFile("file1.txt", ios::ate);     SaveFile << "That's new!/n";     SaveFile.close(); } 正如你在表中所看到的:使用ios::ate将会从文件的末尾开始执行写入。如果我没有使用它,原来的文件内容将会被重新写入的内容覆盖掉。不过既然我已经使用了它,那么我只会在原文件的末尾进行添加。所以,如果file1.txt原有的内容是这样: Hi! This is test from www.cpp-home.com! 那么执行上面的代码后,程序将会为它添上“That’s new!”,因此它看起来将变成这样: Hi! This is test from www.cpp-home.com!That’s new! 假如你打算设置不止一个的打开模式标志,只须使用 OR操作符或者是 |,像这样: ios::ate | ios::binary 我希望现在你已经明白“打开模式”是什么意思了! 现在,是时候向你展示一些真正有用的东西了!我敢打赌你现在还不知道应当怎样打开一个可以同时进行读取和写入操作的文件!下面就是实现的方法: fstream File(“cpp-home.txt”,ios::in | ios::out); 实际上,这只是一个声明语句。我将在下面数行之后给你一个代码示例。但此时我首先想提及一些你应当知道的内容。 上面的代码创建了一个名为“File”的流式文件的句柄。如你所知,它是fstream类的一个对象。当使用fstream时,你应当指定ios::in和ios::out作为文件的打开模式。这样,你就可以同时对文件进行读、写,而无须创建新的文件句柄。噢,当然,你也可以只进行读或者写的操作。那样的话,相应地你应当只使用ios::in或者只使用ios::out—— 要思考的问题是:如果你打算这么做,为什么你不分别用ifstream及ofstream来实现呢? 下面就先给出示例代码: #include <fstream.h> void main()
{     fstream File("test.txt",ios::in | ios::out);     File << "Hi!"; //将“Hi!”写入文件       static char str[10];//当使用static时,数组会自动被初始化                //即是被清空为零


    File.seekg(ios::beg);// 回到文件首部                   // 此函数将在后面解释     File >> str;     cout << str << endl;     File.close();
} OK,这儿又有一些新东西,所以我将逐行进行解释: fstream File(“test.txt”, ios::in | ios::out); —— 此行创建一个fstream对象,执行时将会以读/写方式打开test.txt文件。这意味着你可以同时读取文件并写入数据。 File << “Hi!”; —— 我打赌你已经知道它的意思了。 static char str[10]; —— 这将创建一个容量为10的字符数组。我猜static对你而言或者有些陌生,如果这样就忽略它。这只不过会在创建数组的同时对其进行初始化。 File.seekg(ios::beg); —— OK,我要让你明白它究竟会做些什么,因此我将以一些有点儿离题、但挺重要的内容开始我的解释。 还记得它么: while(!OpenFile.eof())     {        OpenFile.get(ch);        cout << ch;     } 你是不是曾经很想知道那背后真正执行了什么操作?不管是或不是,我都将为你解释。这是一个while型循环,它会一直反复,直至程序的操作到达文件的尾端。但这个循环如何知道是否已经到了文件末尾?嗯,当你读文件的时候,会有一个类似于“内置指针(inside-pointer)”的东西,它表明你读取(写入也一样)已经到了文件的哪个位置,就像记事本中的光标。而每当你调用OpenFile.get(ch)的时候,它会返回当前位置的字符,存储在ch变量中,并将这一内置指针向前移动一个字符。因此下次该函数再被调用时,它将会返回下一个字符。而这一过程将不断反复,直到读取到达文件尾。所以,让我们回到那行代码:函数seekg()将把内置指针定位到指定的位置(依你决定)。你可以使用: ios::beg —— 可将它移动到文件首端 ios::end —— 可将它移动到文件末端 或者,你可以设定向前或向后跳转的字符数。例如,如果你要向定位到当前位置的5个字符以前,你应当写: File.seekg(-5); 如果你想向后跳过40个字符,则应当写: File.seekg(40); 同时,我必须指出,函数seekg()是被重载的,它也可以带两个参数。另一个版本是这样子的: File.seekg(-5,ios::end); 在这个例子中,你将能够读到文件文本的最后4个字符,因为: 1)你先到达了末尾(ios::end) 2)你接着到达了末尾的前五个字符的位置(-5) 为什么你会读到4个字符而不是5个?噢,只须把最后一个看成是“丢掉了”,因为文件最末端的“东西”既不是字符也不是空白符,那只是一个位置(译注:或许ios::end所“指”的根本已经超出了文件本身的范围,确切的说它是指向文件最后一个字符的下一个位置,有点类似STL中的各个容器的end迭代点是指向最后一个元素的下一位置。这样设计可能是便于在循环中实现遍历)。 你现在可能想知道为什么我要使用到这个函数。呃,当我把“Hi”写进文件之后,内置指针将被设为指向其后面……也就是文件的末尾。因此我必须将内置指针设回文件起始处。这就是这个函数在此处的确切用途。 File >> str; —— 这也是新鲜的玩意儿!噢,我确信这行代码让你想起了cin >> .实际上,它们之间有着相当的关联。此行会从文件中读取一个单词,然后将它存入指定的数组变量中。 例如,如果文件中有这样的文本片断: Hi! Do you know me? 使用File >> str,则只会将“Hi!”输出到str数组中。你应当已经注意到了,它实际上是将空格作为单词的分隔符进行读取的。 由于我存入文件中的只是单独一个“Hi!”,我不需要写一个while循环,那会花费更多的时间来写代码。这就是我使用此方法的原因。顺便说一下,到目前为止,我所使用的读取文件的while循环中,程序读文件的方式是一个字符一个字符进行读取的。然而你也可以一个单词一个单词地进行读取,像这样: char str[30];//每个单词的长度不能超过30个字符 while(!OpenFile.eof())     {        OpenFile >> str;        cout << str;     } 你也可以一行一行地进行读取,像这样: char line[100];//每个整行将会陆续被存储在这里
while(!OpenFile.eof())
{ OpenFile.getline(line,100);// 100是数组的大小 cout << line << endl;
} 你现在可能想知道应当使用哪种方法。嗯,我建议你使用逐行读取的方式,或者是最初我提及的逐字符读取的方式。而逐词读取的方式并非一个好的方案,因为它不会读出新起一行这样的信息,所以如果你的文件中新起一行时,它将不会将那些内容新起一行进行显示,而是加在已经打印的文本后面。而使用getline()或者get()都将会向你展现出文件的本来面目! 现在,我将向你介绍如何检测文件打开操作是否成功。实现上,好的方法少之又少,我将都会涉及它们。需要注意的是,出现“X”的时候,它实际可以以“o”、“i”来代替,或者也可以什么都不是(那将是一个fstream对象)。 例1:最通常的作法 Xfstream File(“cpp-home.txt”); if (!File)
{ cout << “Error opening the file! Aborting…/n”; exit(1);
} 例2:如果文件已经被创建,返回一个错误 ofstream File("unexisting.txt", ios::nocreate); if(!File) { cout << “Error opening the file! Aborting…/n”; exit(1); } 例3:使用fail()函数 ofstream File("filer.txt", ios::nocreate); if(File.fail()) { cout << “Error opening the file! Aborting…/n”; exit(1); } 例3中的新出现的东西,是fail()函数。如果有任何输入/输出错误(不是在文件末尾)发生,它将返回非零值。 我也要讲一些我认为非常重要的内容!例如,如果你已经创建一个流文件对象,但你没有进行打开文件操作,像这样: ifstream File; //也可以是一个ofstream 这样,我们就拥有一个文件句柄,但我们仍然没有打开文件。如果你打算迟些打开它,那么可以用open()函数来实现,我已经在本教程中将它介绍了。但如果在你的程序的某处,你可能需要知道当前的句柄是否关联了一个已经打开的文件,那么你可以用is_open()来进行检测。如果文件没有打开,它将返回0 (false);如果文件已经打开,它将返回1 (true)。例如: ofstream File1; File1.open("file1.txt"); cout << File1.is_open() << endl; 上面的代码将会返回1(译注:指File1.is_open()函数,下句同),因为我们已经打开了一个文件(在第二行)。而下面的代码则会返回0,这是由于我们没有打开文件,而只是创建了一个流文件句柄: ofstream File1; cout << File1.is_open() << endl;

检测输入/输出的状态标志 在此我不打算解释“标志(flags)”一词的含义,不过假如你真的完全不理解关于这方面的概念,那么将本章读过一遍之后也许你对此会得到一些认识,我也相信你同样能理解这部分的理论。尽管如此,如果你还是不明白标志在C++中的含义,我推荐你阅读一些关于这个主题的资料。 好,让我们开始吧。 C++中负责的输入/输出的系统包括了关于每一个输入/输出操作的结果的记录信息。这些当前的状态信息被包含在 io_state类型的对象中。 io_state是一个枚举类型(就像open_mode一样),以下便是它包含的值(译注:表中第一列为枚举值的名称,第二列为该值相应含义的描述):

 

godbit 无错误 Eofbit 已到达文件尾 failbit 非致命的输入 / 输出错误 badbit 致使的输入 / 输出错误 有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数,它将返回当前状态的错误标记(上表中提到的)。例如,假如没有任何错误,则rdstate()会返回goodbit. 另一种方法则是使用下面任何一个函数来检测相应的输入/输出状态: bool bad(); bool eof(); // 还记得它么?“不断读取文件内容直到到达文件末尾!” bool fail(); // 噢,这也是老朋友……检测一个打开操作是否成功 bool good(); 假如badbit标志被标设(译注:原文为“If thebadbit flag is up”,这里将“is up”译为“标设”,意即出现了badbit对应的错误,badbit状态被置为当前的错误状态,下同),则bad()函数返回true;假如failbit标志被标设,则fail()函数返回true;假如没有错误发生(goodbit标志被标设),则good()函数返回true;假如操作已经到达了文件末尾(eofbit被标设),则eof()函数返回true. 如果错误发生,你必须清除这些错误状态,以使你的程序能正确适当地继续运行——如果你这么打算的话。要清除错误状态,需使用clear()函数。此函数带一个参数,它是你将要设为当前状态的标志值。假使你想让你的程序“清清爽爽”地运行下去,只要将ios::goodbit作为实参。你将在以下内容中看到示例代码。 我将向你展示示例代码,以巩固你所学到的理论知识。 示例1:简单的状态检测 // 实际应用中可将 FileStream 替换成你相应在使用的文件流句柄     if(FileStream.rdstate() == ios::eofbit)        cout << "End of file!/n";     if(FileStream.rdstate() == ios::badbit)        cout << "Fatal I/O error!/n";     if(FileStream.rdstate() == ios::failbit)        cout << "Non-fatal I/O error!/n";     if(FileStream.rdstate() == ios::goodbit) cout << "No errors!/n"; 示例2: clear()函数 #include <fstream.h> void main() {     ofstream File1("file2.txt"); // 建立 file2.txt     File1.close();     // 下面的检测代码将会返回错误,这是因为我使用了 ios::noreplace 打开模式     // 它模式在试图打开一个已存在的文件时会返回错误     ofstream Test("file2.txt",ios::noreplace);     // 上一行将导致 ios::failbit 错误,我们这就将其演示出来     if(Test.rdstate() == ios::failbit)        cout << "Error...!/n";     Test.clear(ios::goodbit); // 将当前状态重置为 ios::goodbit     if(Test.rdstate() == ios::goodbit) // 检测程序是否已经正确地施行了设置        cout << "Fine!/n";     Test.clear(ios::eofbit); // 将状态标志设为 ios::eofbit. 无实际用途 .     if(Test.rdstate() == ios::eofbit) // 检测是否已经正确地施行了设置        cout << "EOF!/n";     Test.close();    } 除了使用标记值判断,你也可以使用函数(译注:指bad()、eof()、fail()、good()这些函数)的形式进行判断,两者实际上是一样的——都是检测某个标记是否被标设。这些函数前面已经介绍过,我就不再重复了。如果你对如何使用它们还不是十分确定,那就重新回顾一下本教程中我曾经为你演示的应该如何检测一个文件打开操作是否成功的那部分内容。在那里我就使用了fail()函数。 二进制文件的处理 虽然有规则格式(formatted)的文本(到目前为止我所讨论的所有文件形式)非常有用,但有时候你需要用到无格式(unformatted)的文件——二进制文件。它们和你的可执行程序看起来一样,而与使用<<及>>操作符创建的文件则大不相同。get()函数与put()函数则赋予你读/写无规则格式文件的能力:要读取一个字节,你可以使用get()函数;要写入一个字节,则使用put()函数。你应当回想起get()——我曾经使用过它。你可能会疑惑为什么当时我们使用它时,输出到屏幕的文件内容看起来是文本格式的?嗯,我猜这是因为我此前使用了<<及>>操作符。 译注:作者的所谓“规则格式文本(formatted text)”即我们平时所说的文本格式,而与之相对的“无格式文件(unformatted files)”即以存储各类数据或可执行代码的非文本格式文件。通常后者需要读入内存,在二进制层次进行解析,而前者则可以直接由预定好的<<及>>操作符进行读入/写出(当然,对后者也可以通过恰当地重载<<及>>操作符实现同样的功能,但这已经不是本系列的讨论范围了)。 get()函数与都各带一个参数:一个char型变量(译注:指get()函数)或一个字符(译注:指put()函数,当然此字符也可以以char型变量提供)。 假如你要读/写一整块的数据,那么你可以使用read()和write()函数。它们的原型如下: istream &read(char *buf, streamsize num); ostream &write(const char *buf, streamsize num); 对于read()函数,buf应当是一个字符数组,由文件读出的数据将被保存在这儿。对于write()函数,buf是一个字符数组,它用以存放你要写入文件的数据。对于这两个函数,num是一个数字,它指定你要从文件中读取/写入的字节数。 假如在读取数据时,在你读取“num”个字节之前就已经到达了文件的末尾,那么你可以通过调用gcount()函数来了解实际所读出的字节数。此函数会返回最后一次进行的对无格式文件的读入操作所实际读取的字节数。 在给出示例代码之前,我要补充的是,如果你要以二进制方式对文件进行读/写,那么你应当将ios::binary作为打开模式加入到文件打开的参数表中。 现在就让我向你展示示例代码,你会看到它是如何运作的。 示例1:使用get( )和put( ) #include <fstream.h> void main() {     fstream File("test_file.txt",ios::out | ios::in | ios::binary);     char ch;     ch='o';     File.put(ch); //将ch的内容写入文件     File.seekg(ios::beg);//定位至文件首部     File.get(ch); //读出一个字符     cout << ch << endl;//将其显示在屏幕上     File.close(); } 示例2:使用read( )和write( ) #include <fstream.h> #include <string.h> void main() {     fstream File("test_file.txt",ios::out | ios::in | ios::binary);     char arr[13];     strcpy(arr,"Hello World!");//将Hello World!存入数组     File.write(arr,5);//将前5个字符——"Hello"写入文件     File.seekg(ios::beg);//定位至文件首部     static char read_array[10];//在此我将打算读出些数据     File.read(read_array,3);//读出前三个字符——"Hel"     cout << read_array << endl;//将它们输出      File.close(); } tellg()——返回一个int型数值,它表示“内置指针”的当前位置。此函数仅当你在读取一个文件时有效。例如:
    #include <fstream.h>
    
    void main()
    {
        //假如我们已经在test_file.txt中存有了“Hello”的内容
        ifstream File("test_file.txt");
    
        char arr[10];
    
        File.read(arr,10);
       
        //由于Hello占5个字符,因此这里将返回5
        cout << File.tellg() << endl;
    
        File.close();
    }
   
    tellp() —— 与tellg()有同样的功能,但它用于写文件时。总而言之:当我们读取一个文件,并要知道内置指针的当前位置时,应该使用tellg();当我们写入一个文件,并要知道内置指针的当前位置时,应该使用tellp(). 由于此函数的用法与tellg()完全一样,我就不给出示例代码了。
   
    seekp() —— 还记得seekg()么?当我在读取一个文件,并想到达文件中某个特定位置时,就曾使用过它。seekp()亦如此,只不过它用于写入一个文件的时候。例如,假如我在进行文件读写,而要定位到当前位置的三个字符之前,则需调用FileHandle.seekg(-3). 但如果我是在写入一个文件,并且比如我要重写后5个字符的内容,我就必须往回跳转5个字符,因而,我应该使用FileHandle.seekp(-5) .
   
    ignore() —— 使用于读取文件之时。如果你想略过一定数量的字符,只需使用此函数。实际上,你也可以使用seekg()来代替,然而使用ignore()有一个优点——你可以指定一个特定“界限规则(delimiter rule)”,同样使得ignore()在指定的位置停下。函数原型如下:
   
    istream& ignore( int nCount, delimiter );
   
    nCount表示要略过的字符数量,而delimiter—— 与它的名称有着同样的含义:假如你想在文件末尾停下,则可使用EOF值传入,这样一来此函数就等同于seekg();但该参数还可以使用其他值,例如‘/n’这样可以在换行的同时定位在新行处。下面是示例:
    #include <fstream.h>
    
    void main()
    {
        //假设test_file.txt中已经存有"Hello World"这一内容
        ifstream File("test_file.txt");
    
        static char arr[10];
    
        //假如一直没有遇到字符"l",则向前定位直到跳过6个字符
        //而如果期间遇到"l",则停止向前,定位在该处
        File.ignore(6,'l');
    
        File.read(arr,10);
    
        cout << arr << endl; //它将显示"lo World!"
    
        File.close();
    
    }
   
    getline()——虽然前面的章节中我曾提到过这个函数,但还有一些内容我们未曾涉及:此函数不但可用于逐行读取,而且它还可以设为遇到某个特定字符后停止读取。下面给出传递这一参数的方法:
   
    getline(array,array_size,delim);
   
    以下为示例代码:
   
    #include <fstream.h>
    
    void main()
    {
        //假设test_file.txt中已经存有"Hello World"这一内容
        ifstream File("test_file.txt");
    
        static char arr[10];
    
        /*读取,直到满足下面的条件之一:
    1)已经读取10个字符
    2)遇到字母"o"
    3)出现新一行
        */
        File.getline(arr,10,'o');
    
        cout << arr << endl; //将显示"Hell"
        File.close();
    }
   
    peek() —— 此函数将返回输入流文件的下一个字符,但它不移动内置指针。我想你该记得,像get()这样的函数也返回输入流文件的下一个字符,而与此同时它将移动内置指针。所以当你再次调用get()函数的时候,它会返回再下一个字符,而非前面那个。哦,使用peek()也会返回字符,但它不会移动“光标”。所以,假如你连续两次调用peek()函数,它会返回同一个字符。考虑以下代码:
   
    #include <fstream.h>
    
    void main()
    {
        //假设test_file.txt中已经存有"Hello World"这一内容
        ifstream File("test_file.txt");
    
        char ch;
    
        File.get(ch);
        cout << ch << endl; //将显示"H"
    
        cout <<    char(File.peek()) << endl; //将显示"e"
        cout <<    char(File.peek()) << endl; //将再次显示"e"
    
        File.get(ch);
        cout << ch << endl; //还是显示"e"
   
    File.close();
       
    }
   
    顺便说一下,我忘了讲——peek()函数实质上返回的是字符的ASCII码,而非字符本身。因此,假如你想看到字符本身,你得像我在示例中做的那样进行调用(译注:即要转为char类型)。
   
    _unlink() —— 删除一个文件。假如你要使用此函数,需要在你的程序中包含io.h头文件。下面是示例代码:
   
    #include <fstream.h>
    #include <io.h>
    
    void main()
    {
        ofstream File;
    
        File.open("delete_test.txt"); //创建一个文件
        File.close();
    
        _unlink("delete_test.txt"); //删除这个文件
   
        //试图打开此文件,但假如它已不存在
        //函数将返回一个ios::failbit错误值
        File.open("delete_test.txt",ios::nocreate);
    
        //验证它是否返回该值
        if(File.rdstate() == ios::failbit)
            cout << "Error...!/n"; //耶,成功了
   
    File.close();
    
    }
   
    putback()—— 此函数将返回最后一个所读取字符,同时将内置指针移动-1个字符。换言之,如果你使用get()来读取一个字符后再使用putback(),它将为你返回同一个字符,然而同时会将内置指针移动-1个字符,所以你再次使用get()时,它还是会为你返回同样的字符。下面是示例代码:
   
    #include <fstream.h>
    
    void main()
    {
        //test_file.txt应包含内容"Hello World"
        ifstream File("test_file.txt");
       
        char ch;
    
        File.get(ch);
    
        cout << ch << endl; //将显示"H"
    
        File.putback(ch);
        cout << ch << endl; //仍将显示"H"
       
        File.get(ch);
        cout << ch << endl; //再一次显示"H"
    
        File.close();
    }
   
    flush() —— 在处理输出流文件的时候,你所存入的数据实际上并非立刻写入文件,而是先放入一个缓冲区中,直到该缓冲区放满数据之后,这些数据才被存入真正的文件中(在你的磁盘上)。旋即缓冲区会被清空,再重新进行下一轮写入。
    但假如你想在缓冲区写满之前就将其中的数据写入磁盘,则使用flush()函数。只须像这样进行调用:FileHandle.flush(),这样缓冲区内的数据将会写入实际的物理文件,而后缓冲区被清空。
    再补充一点(高阶的)内容:flush()函数会调用与相应流缓冲(streambuf)相联系的sync()函数(出自MSDN)。
   
    结语
   

    嗯,我希望你现在可以实现你的文件输入/输出程序了。我已经将自己所知的内容都涉及到,我想它们比你所需要的还要多。即使如此,还是会有一些内容我没有提及的……不过我想你或许都不会使用到那些方面的内容。因此,如果你还需要了解更多关于某些特定主题的高阶的理论,可以在互联网上搜索,例如,可以尝试一下 google.com,但你可别来问我!我不对任何询问我关于如何实现某个程序此类问题的邮件作答复。
    假如你喜欢这个系列的教程,或者相反,我都会非常高兴能看到你的意见。所以请随意地在网上联系我: loobian@cpp-home.com
    想获取更多的C++教程,请访问: www.cpp-home.com

 

C++读取文件txt,循环逐行输出(转)

笔记:C++文件的读取和写入


#include <iostream>
#include <iomanip>
#include <fstream>

using namespace std;

int main(){
char buffer[256];
ifstream myfile ("c:\\a.txt");
ofstream outfile("c:\\b.txt");

if(!myfile){
  cout << "Unable to open myfile";
        exit(1); // terminate with error

}
if(!outfile){
    cout << "Unable to open otfile";
        exit(1); // terminate with error

}
int a,b;
int i=0,j=0;
int data[6][2];
  while (! myfile.eof() )
  {
     myfile.getline (buffer,10);
    sscanf(buffer,"%d %d",&a,&b);
    cout<<a<<" "<<b<<endl;
     data[i][0]=a;
     data[i][1]=b;
     i++;
  }
myfile.close();
  for(int k=0;k<i;k++){
      outfile<<data[k][0] <<" "<<data[k][1]<<endl;
     cout<<data[k][0] <<" "<<data[k][1]<<endl;
  }

outfile.close();
return 0;
}

无论读写都要包含<fstream>头文件

读:从外部文件中将数据读到程序中来处理
对于程序来说,是从外部读入数据,因此定义输入流,即定义输入流对象:ifsteam infile,infile就是输入流对象。
这个对象当中存放即将从文件读入的数据流。假设有名字为myfile.txt的文件,存有两行数字数据,具体方法:
int a,b;
ifstream infile;
infile.open("myfile.txt");      //注意文件的路径
infile>>a>>b;                   //两行数据可以连续读出到变量里
infile.close()

如果是个很大的多行存储的文本型文件可以这么读:
char buf[1024];                //临时保存读取出来的文件内容
string message;
ifstream infile;
infile.open("myfile.js");
if(infile.is_open())          //文件打开成功,说明曾经写入过东西
{
while(infile.good() && !infile.eof())
{
    memset(buf,0,1024);
    infile.getline(buf,1204);
    message = buf;
    ......                     //这里可能对message做一些操作
    cout<<message<<endl;
}
infile.close();
}

写:将程序中处理后的数据写到文件当中
对程序来说是将数据写出去,即数据离开程序,因此定义输出流对象ofstream outfile,outfile就是输出流对象,这个对象用来存放将要写到文件当中的数据。具体做法:
ofstream outfile;
outfile.open("myfile.bat"); //myfile.bat是存放数据的文件名
if(outfile.is_open())
{
outfile<<message<<endl;    //message是程序中处理的数据
   outfile.close();
}
else
{
   cout<<"不能打开文件!"<<endl;
}


c++对文件的读写操作的例子

/*/从键盘读入一行字符,把其中的字母依次放在磁盘文件fa2.dat中,再把它从磁盘文件读入程序,
将其中的小写字母改成大写字母,再存入磁盘fa3.dat中*/
#i nclude<fstream>
#i nclude<iostream>
#i nclude<cmath>
using namespace std;
//////////////从键盘上读取字符的函数
void read_save(){
      char c[80];
      ofstream outfile("f1.dat");//以输出方工打开文件
      if(!outfile){
                   cerr<<"open error!"<<endl;//注意是用的是cerr
                   exit(1);
                   }
          cin.getline(c,80);//从键盘读入一行字符
          for(int i=0;c[i]!=0;i++) //对字符一个一个的处理,直到遇到'/0'为止
                if(c[i]>=65&&c[i]<=90||c[i]>=97&&c[i]<=122){//保证输入的字符是字符
                   outfile.put(c[i]);//将字母字符存入磁盘文件
                   cout<<c[i]<<"";
                   }
                   cout<<endl;
                   outfile.close();
                   }
void creat_data(){
      char ch;
      ifstream infile("f1.dat",ios::in);//以输入的方式打开文件
      if(!infile){
                  cerr<<"open error!"<<endl;
                  exit(1);
                  }
    ofstream outfile("f3.dat");//定义输出流f3.dat文件
    if(!outfile){
                 cerr<<"open error!"<<endl;
                 exit(1);
                 }
     while(infile.get(ch)){//当读取字符成功时
     if(ch<=122&&ch>=97)
     ch=ch-32;
     outfile.put(ch);
     cout<<ch;
     }
     cout<<endl;
     infile.close();
     outfile.close();
     }
     int main(){
         read_save();
         creat_data();
        system("pause");
         return 0;
         }

 

 

 

 

 

 

关于初始化C++类成员

 

在使用C++编程的过程当中,常常需要对类成员进行初始化,通常的方法有两种:

第一种方法:

view source print ? 1. CMYClass::CSomeClass() 2. { 3.     x=0; 4.     y=1; 5. }

第二种方法:

view source print ? 1. CSomeClass::CSomeClass() : x(0), y(1) 2. { 3. }

本文将要探讨这两种方法的异同以及如何使用这两种方法。

从技术上说,第二种方法比较好,但是在大多数情况下,两者实际上没有什么区别。第二种语法被称为成员初始化列表,之所以要使用这种语法有两个原因:一个原因是必须这么做,另一个原因是出于效率考虑。

让我们先看一下第一个原因——必要性。设想你有一个类成员,它本身是一个类或者结构,而且只有一个带一个参数的构造函数。

view source print ? 1. class CMember { 2. public : 3.     CMember( int x) { ... } 4. };

因为CMember有一个显式声明的构造函数,编译器不产生一个缺省构造函数(不带参数),所以没有一个整数就无法创建CMember的一个实例。

view source print ? 1. CMember* pm = new CMember;        // 出错!! 2. CMember* pm = new CMember(2);     // OK

如果CMember是另一个类的成员,你怎样初始化它呢?答案是你必须使用成员初始化列表。

view source print ? 01. class CMyClass { 02.     CMember m_member; 03. public : 04.     CMyClass(); 05. }; 06. // 必须使用初始化列表来初始化成员 m_member 07. CMyClass::CMyClass() : m_member(2) 08. 09. { 10. ••• 11. }

没有其它办法将参数传递给m_member,如果成员是一个常量对象或者引用也是一样。根据C++的规则,常量对象和引用不能被赋值,它们只能被初始化。

使用初始化列表的第二个原因是出于效率考虑,当成员类具有一个缺省的构造函数和一个赋值操作符时。MFC的CString提供了一个完美的例子。假定你有一个类CMyClass具有一个CString类型的成员m_str,你想把它初始化为"Hi,how are you."。你有两种选择:

view source print ? 1. CMyClass::CMyClass() { 2. // 使用赋值操作符 3. // CString::operator=(LPCTSTR); 4. m_str = _T( "Hi,how are you." ); 5. } view source print ? 1. // 使用初始化列表 2. // 和构造函数 CString::CString(LPCTSTR) 3. CMyClass::CMyClass() : m_str(_T( "Hi,how are you." )) 4. { 5. }

在它们之间有什么不同吗?是的。编译器总是确保所有成员对象在构造函数体执行之前被初始化,因此在第一个例子中编译的代码将调用CString::Cstring来初始化m_str,这在控制到达赋值语句前完成。在第二个例子中编译器产生一个对CString:: CString(LPCTSTR)的调用并将"Hi,how are you."传递给这个函数。结果是在第一个例子中调用了两个CString函数(构造函数和赋值操作符),而在第二个例子中只调用了一个函数。

在CString的例子里这是无所谓的,因为缺省构造函数是内联的,CString只是在需要时为字符串分配内存(即,当你实际赋值时)。但是,一般而言,重复的函数调用是浪费资源的,尤其是当构造函数和赋值操作符分配内存的时候。在一些大的类里面,你可能拥有一个构造函数和一个赋值操作符都要调用同一个负责分配大量内存空间的Init函数。在这种情况下,你必须使用初始化列表,以避免不要的分配两次内存。

在内建类型如ints或者longs或者其它没有构造函数的类型下,在初始化列表和在构造函数体内赋值这两种方法没有性能上的差别。不管用那一种方法,都只会有一次赋值发生。有些程序员说你应该总是用初始化列表以保持良好习惯,但我从没有发现根据需要在这两种方法之间转换有什么困难。在编程风格上,我倾向于在主体中使用赋值,因为有更多的空间用来格式化和添加注释,你可以写出这样的语句:

view source print ? 1. x=y=z=0;

或者

view source print ? 1. memset ( this ,0, sizeof ( this ));

注意第二个片断绝对是非面向对象的。

当我考虑初始化列表的问题时,有一个奇怪的特性我应该警告你,它是关于C++初始化类成员的,它们是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

view source print ? 1. class CMyClass { 2.     CMyClass( int x, int y); 3.     int m_x; 4.     int m_y; 5. }; 6. CMyClass::CMyClass( int i) : m_y(i), m_x(m_y) 7. { 8. }

你可能以为上面的代码将会首先做m_y=i,然后做m_x=m_y,最后它们有相同的值。但是编译器先初始化m_x,然后是m_y,,因为它们是按这样的顺序声明的。结果是m_x将有一个不可预测的值。这个例子是故意这样设计来说明这一点的,然而这种bug会很自然地出现。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序来声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

 

 

 

推荐:C++ IO标准库 文件操作

转自:http://hi.baidu.com/luckymouse2009/blog/item/80042808ee129da62fddd47e.html       C++ IO标准库 文件操作 2009-07-01 14:23 写一个在一个流中同时读写

多线程编程之一——问题提出(http://www.vckbase.com/index.php/wv/1412) 多线程编程之二——MFC中的多线程开发(http://www.vckbase.com/index.php/wv/1414) 多线程编程之三——线程间通讯(http

相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。