目录
流类库:
流类库三大类:
流类库的继承关系:
流类库三大头文件:
ios类
标准I/O
通用输入流类istream:
istream类中函数及cin使用方式
通用输出流类ostream:
ostream类中函数及cout使用方式:
流错误状态函数和状态字
状态字
流错误状态函数
格式控制
文件I/O
文本文件特点
二进制文件特点
文件的基本操作
文件输入/输出流类
文件输出流类ofstream
文件输入流类ifstream
检查输入文件状态
文件输入输出流类fstream
open()函数
文件的随机读写
流类库:原始数据需要用户输入到计算机,而处理结果则需要输出设备反馈给用户。最常见的输出设备是键盘,称为标准输入。常用的输出设备是显示器,称为标准输出。数据在键盘,计算机和显示器流动的过程看作是一个数据流动的过程。提供输入数据的数据源称为输入数据流;将输出时的目的地称为输出数据流,输入数据流和输入数据流统称为输入输出流!
C语言的使用的输入输出函数,而C++使用的输入输出流类。
第一部分首先了解流的大体的知识体系
流类库三大类:这三类均以ios类为基类的类族
1、通用输入/输出流类:提供通用的输入输出(简称标准I/O)功能。
2、文件输入/输出流类:提供文件输入输出(简称文件I/O)功能。
3、字符串输入/输出流类:提供字符串输入输出(简称字符串I/O)功能。
C++通过不同的流类库定义不同的流对象,通过访问流对象及其成员达到输入输出功能。(需要包含头文件)
流类库的继承关系:
流类库三大头文件:
1、iostream。包含操作所有输入输出所需的基本信息,因此大多数C++程序都应该包含这个头文件。该文件包含cin(标准输入流),cout(标准输出流),cerr(非缓冲错误输出流),clog(缓冲错误输出流)4个预定义对象,同时提供了无格式和格式化的I/O功能。
2、iomanip。头文件包含格式化I/O的带参数操纵算子。这些算子用于指定数据输入/输出的格式。
3、fstream。头文件包含处理文件的有关信息,提供建立文件读写文件的各种操作接口。
ios派生出三大类:标准输入流类,标准输入输出流类,标准输出流类。每一类又派生出文件输入/输出流类(f起首)和字符串输入/输出流类(string起首),注意iostream继承了istream和ostream。不同C++编译器提供的流类库接口上是一致的,均符合C++标准,但其内部实现形式有所不同。
ios类
ios类作为最底层的基类,其数据成员枚举变量和成员函数用来控制输出格式,这里给出基类ios的 示意代码,后期输入输出流中会用到其成员数据:
class ios // 基类ios的声明部分
{
public:
enum fmtFlags
{ // 枚举类型fmtFlags:定义用作格式标记的枚举常量
skipws = 0x0001, // 输入时跳过空白字符(空格、制表符、回车或换行符)
left = 0x0002, // 输出时左对齐,不足部分在右侧补填充字符
right = 0x0004, // 输出时右对齐,不足部分在左侧补填充字符
internal = 0x0008, // 输出时在正负号或数制后面补填充字符
dec = 0x0010, // 输入/输出时,将整数按十进制进行转换
oct = 0x0020, // 输入/输出时,将整数按八进制进行转换
hex = 0x0040, // 输入/输出时,将整数按十六进制进行转换
showbase = 0x0080, // 输出时包含指示数制的基字符(例如0或0x)
showpoint = 0x0100, // 输出浮点数时带小数点和小数0
uppercase = 0x0200, // 输出十六进制数时用大写字母
showpos = 0x0400, // 输出正数时带正号
scientific = 0x0800, // 输出浮点数时采用科学表示法
fixed = 0x1000, // 输出浮点数时不采用科学表示法,即定点格式
};
enum open_mode
{ // 枚举类型open_mode:定义用作打开模式的枚举常量
in = 0x01, out = 0x02, ate = 0x04, app = 0x08, trunc = 0x10, binary = 0x80
};
enum seek_dir
{ // 枚举类型seek_dir:定义用作文件指针移动基准的枚举常量
beg = 0, cur = 1, end = 2
};
inline long flags(long _l); // 设置输入/输出时的格式标记,内联函数
inline long flags() const; // 返回当前的格式标记,内联函数,常函数成员
inline int width(int _i); // 设置下一个数据在输入/输出时的位数,内联函数
inline int width() const; // 返回当前的输入/输出位数,内联函数,常函数成员
inline char fill(char _c); // 设置输出时的填充字符,内联函数
inline char fill() const; // 返回当前的填充字符,内联函数,常函数成员
inline int precision(int _i); // 设置浮点数输出时的小数位数,内联函数
inline int precision() const; // 返回浮点数输出时的小数位数,内联函数,常函数成员
inline int good() const; // 返回输入/输出流状态是否正常,内联函数,常函数成员
inline int eof() const; // 返回输入/输出流是否结束,内联函数,常函数成员
locale imbue(const locale& loc); // 设置本地语言(例如中文、英文等)
virtual ~ios(); // 虚析构函数
protected:
streambuf* bp; // 流缓冲区指针
ios( ); // 无参构造函数
private:
ios( const ios& ); // 拷贝构造函数
ios& operator=( const ios& ); // 重载赋值运算符
// ...... 以下代码省略
} ;
ios基类中的枚举类型和函数再后面会用到,这些东西不求全记忆,用时即查。
此外系统输入输出时,数据会先再内存缓存区缓存,而不是立即输入输出,具体如下:
--输入过程:计算机输入的数据先被存到内存缓存区,当按下回车键的时候,cin再从内存缓冲区读出数据,进格式转换,再赋值给相应的变量。
--输出过程:先将输出数据存放到内存缓存区,当缓存区满的时候,再将数据输出到显示器中
标准I/O
标准I/O的 I 指的是键盘输入,O是指显示器输出。使用流类库输入输出就是用流类库中的类定义流对象,通过访问其成员,实现特定的输入输出功能。C++流类库预先定义好了代表键盘的输入流对象cin和代表显示器的输出流对象cout。均被定义在std命名空间当中。所以程序员包含头文件iostream和命名空间std后就可以直接使用cin/cout。
namespace std//命名空间
{
istream cin;//键盘对象cin
ostream cout;//显示器对象cout
}
cin>>x;//输入数据流,位移运算符表示数据从键盘对象cin流向内存变量x
cout<
这里给出通用输入流类istream的 示意代码:
class istream : virtual public ios // 公有继承虚基类ios
{
public:
istream& operator>>(char *); // 以下代码为重载右移运算符“>>”
inline istream& operator>>(unsigned char *);
inline istream& operator>>(signed char *);
istream& operator>>(char &);
inline istream& operator>>(unsigned char &);
inline istream& operator>>(signed char &);
istream& operator>>(short &);
istream& operator>>(unsigned short &);
istream& operator>>(int &);
istream& operator>>(unsigned int &);
istream& operator>>(long &);
istream& operator>>(unsigned long &);
istream& operator>>(float &);
istream& operator>>(double &);
istream& operator>>(long double &);
int get(); // 以下代码为新增函数成员get
inline istream& get(unsigned char &);
inline istream& get( signed char &);
inline istream& get(unsigned char *,int,char ='n');
inline istream& get( signed char *,int,char ='n');
inline istream& getline(unsigned char *,int,char ='n');
// 以下代码为新增函数成员getline
inline istream& getline( signed char *,int,char ='n');
inline istream& read(unsigned char *,int); // 以下代码为新增函数成员read
inline istream& read(signed char *,int);
int gcount() const; // 新增函数成员gcount
istream& seekg(long); // 以下代码为新增函数成员seekg
istream& seekg(long,ios::seek_dir);
long tellg(); // 新增函数成员tellg
// ...... 以下代码省略
};
cin作为std命名空间下预定义的对象,cin>>x。实际上就相当于对象cin调用成员函数,而成员函数内部实现数据从缓存区流向变量下。输入流类istream类中代码设计的函数种类有:operator>>,get(),getline(),read(),seekg(),tellg()。这些函数调用时,和类对象调用函数时是相同。
istream类中函数及cin使用方式
cin用法:
cin作为std命名空间下预定义的对象,它是一种格式化的输入方法。首先,键盘输入的字符先存入缓存区中,当用户按下回车,cin再从缓冲区中提取字符,经格式转换后赋值给输入变量。如果再一行中输入多个变量,则需要用空格,TAB键或换行符隔开(这三个字符都是流提取时的分隔符)。(实际上就是一次性把要输入的信息输送到缓存区当中,等待对象cin来提取,遇到空格则停止提取,等到下一个cin语句再继续提取,而输入流对象从流中提取数据并写入内存的过程叫做输入流操作)当遇到输入流中的文件结束符时,流提取对象返回false并结束提取;否则返回输入流对象的引用,继续提取(所以流提取运算符>>可以连用)。
具体使用的细节,看代码!
例一:
#includeusing namespace std; int main() { int x = 0; double y = 0; cout << "输入:";cin >> x >> y; cout << "输出:";cout << "x=" << x << ", y=" << y << endl; return 0; }
不同的输入和输出结果:
当输入变量和赋值的变量类型不匹配,就不会赋值!第一个输出结果正确,第二个cin先将15转化为整型赋值给x,将0.234转化为实数(浮点数)赋值给y,最终19任然再缓冲区!第三个类型不匹配,所以不会赋值。所以总结:使用键盘输入数据时,用户输入的数据应与程序中变量的类型,个数,顺序一致,否则,可能导致错误结果。
例二:
#includeusing namespace std; int main() { char str1[5], str2[8]; cin >> str1 >> str2; cout << str1 << str2; }
首先字符串的结尾需要又字符串结束符‘ ’,程序中str1只能填4个位置,剩一个给‘n’,如用户输入的字符超过最大的输入空间,会出现越界错误(会报错)!为了避免出现这样的错误,可以用cin的成员函数限制输入字符的个数,很细节的东西。如下:
cin.width( 5 ); // 设置下一输入项的最大字符个数为5(含字符串结束符) cin >> str1; // 使用键盘对象cin的提取运算符输入str1,最多提取4个字符 cin.width( 8 ); // 设置下一个输入项的最大字符个数为8(含字符串结束符) cin >> str2;
这时的输入输出结果有:
这些结果原因总结:缓存区有数据就提取数据,没数据就等待数据。遇到空格停止读取!所以即使有了width,虽然不会报错,但会出现错误结果。
istream函数
C++为了有时候能对输入流经行更加灵活的操作,再istream类中定义了各类函数,供程序员用不同方法从输入流中提取数据,有read(),get(),getline(),ignore(),peek(),gcount(),eatwhile(),seekg(),tell(),operator>>等等,具体用法如下:
1、get函数(三种重载形式):
--int get();不带参数的get函数从指定输入流中提取一个字符(包括空白字符),并将其作为函数调用的返回值,常常作为右值赋值给另外的变量(c=cin.get(c);)当遇到文件结束符时,返回系统常量EOF。
--istream& get(char&rch);该函数从指定输入流中提取一个字符(包括空白字符),并将字符写入rch引用的对象中,当遇到文件结束符时返回0;否则返回istream对象的引用,继续提取。
--istream&get(char*pch,int nCount,char delim='n');该函数从六当前字符开始,读取nCount-1个字符,或遇到指定分隔符delim后结束。该函数吧读取的字符(不包括分隔符)写入数组pch中,并在字符串后添加' '。
2、getline函数
istream&getline(char*pch,int nCount,char delim='n');从流种提取nCount-1个字符,或遇到指定分隔符delim后结束。该函数吧读取的字符(不包括分隔符)写入数组pch中,并在字符串后添加' '。
3、read函数
istream&read(char*pch,int nCount);该函数从流种读出的字节序列赋给pch(地址)所指向的内存字节序列,参数nCount指定提取的字节数。该函数是非格式化操作,对读取的字节序列不进行处理,送到指定内存单元后由程序的类型定义解释。(不同于get和getline函数,read读的是字节!)
通用输出流类ostream:
这里给出通用输出流类ostream的 示意代码:
class ostream : virtual public ios // 公有继承虚基类ios
{
public:
// 以下代码为重载左移运算符“<<”
inline ostream& operator<<(ostream& (__cdecl * _f)(ostream&));
inline ostream& operator<<(ios& (__cdecl * _f)(ios&));
ostream& operator<<(const char *);
inline ostream& operator<<(const unsigned char *);
inline ostream& operator<<(constsigned char *);
inline ostream& operator<<(char);
ostream& operator<<(unsigned char);
inline ostream& operator<<(signed char);
ostream& operator<<(short);
ostream& operator<<(unsigned short);
ostream& operator<<(int);
ostream& operator<<(unsigned int);
ostream& operator<<(long);
ostream& operator<<(unsigned long);
inline ostream& operator<<(float);
ostream& operator<<(double);
ostream& operator<<(long double);
ostream& operator<<(const void *);
ostream& operator<<(streambuf*);
inline ostream& put(char); // 以下代码为新增函数成员put
ostream& put(unsigned char);
inline ostream& put(signed char);
ostream& write(const char *,int); // 以下代码为新增函数成员write
inline ostream& write(const unsigned char *,int);
inline ostream& write(constsigned char *,int);
ostream& seekp(long); // 以下代码为新增函数成员seekp
ostream& seekp(long,ios::seek_dir);
long tellp(); // 新增函数成员tellp
ostream& flush(); // 新增函数成员flush:立即输出,然后清空缓冲区
// ...... 以下代码省略
} ;
cout作为std命名空间预定义的对象,cout< cout用法: 把内存中的数据插入输出流中的过程称为输出流操作。常用输出流运算符<<输出C++基本数据类型的数据。由于插入运算符<<函数返回调用该对象的引用,所以插入流运算符可以连用。此外使用<<插入运算符时,首先会将各种不同类型的数据类型转换成字符串格式,然后再输出。cout插入运算符时一种格式化的输出方法,使用插入运算符时可以设定数据的输出格式,包括输出位数,对齐方式,精度等等。C++流类库提供了两种,分别时格式标记和格式操纵符。输出格式设定后,只要输出格式没有改变,后面的输出都按开头的格式输出。 ostream类中函数 1、put函数: ostream&put(char ch);将内存中一个字节的字符插入到输出流中,并返回输出流对象的引用。put是非格式化输出方法,只能输出单个字符。 2、write函数 ostream&write(const char*pch,int nCount);该函数向流中插入pch所指向的字节序列,参数nCount指定字节数。(read也是对应字节数) 再ios类中,定义了一个记录流错误状态的数据成员,称为状态字。每个状态字代表一种错误状、态。如下: 此外ios类还有几个与错误状态有关的公有函数,程序员使用这些函数时不许要数值状态字的特定位值。 1、int eof()const; 该函数返回eofbit状态值。当遇到文件结束符时,在输入流中自动设置eofbit。此外,可以在程序中用eof函数测试是否达到文件尾。例如:cin.eof()遇到文件结束符时返回1,否则返回0.可以按下Ctrl+Z组合键,表示标准输入流结束。 2、int fail() const; 4、int operator void*(); 上述两个函数,如果bad、fail和eof函数全部返回 false,即 eofbit、 failbit和 badbit都没有 5、int bad() const; 6、int operator!(); 上述两个函数,ofit. failbit或 badbit中一个被设置,则返回1,否则返回0。 7、int rdstate() const; 该函数返回状态字。例如,函数调用: cout.rdstate();将返回流的当前状态。随后可以用位测试的方法检查各种错误状态。 8、void clear( int nState =0); 该函数恢复或设置状态字。默认参数为0,即 ios::goodbit,对状态字清0,把流状态恢复为 为了满足用户对数据输入/输出的格式化要求,ios类提供了直接设置标志字的控制格式函数 1、标志常量 2、ios类中控制格式的函数 3、格式控制符 例子一: 输出结果1: 例子二: 例子三: 对于标准I/O,是键盘输入,显示器输出。而文件I/O则是文件输入,文件输出。 首先清楚文件分类和文件I/O所指: 一个完整的文件名格式:盘符:目录名子目录名.....文件名.扩展名(如:C:WIndownotepad.exe) 文件格式:文本文件,二进制文件。 1、 存储字符编码。文本文件所存储的内容是一个字符序列。每个字符所存储的是其字符编码,例如英文字母存储的是其ASCII编码(1个字节),中 文字符存储的是其机内码也称为汉字ASCll码(2个字节) 2、具有换行格式。文本换行时,存储2个控制字符CR(ASCII编码为13)和LF (ASCII编码为10) 3、 通用性强。文本文件存储的是纯文本内容,而且使用的是标准编码。文本文件不含任何其它附加信息(例如字体、排版格式等)。阅读文本文 件不需要安装特殊的软件,使用类似于“记事本”这样的常规软件就能 阅读。文本文件的阅读、修改等不依赖于某个特定的软件。换句话说, 文本文件的通用性强 4、 可用于数据交换。文本文件通用性强,可用于数据交换。例如,一个程序的处理结果可以通过文本文件输出给人来阅读,这是程序-人之间的数 据交换;一个程序的处理结果可以通过文本文件输入给另一个程序,这是程序-程序之间的数据交换;一个操作系统中程序的处理结果可以通过文本文件传送给另一个操作系统中的程序,这是操作系统-操作系统之间的数据交换。 二进制数据文件是以数据再内存的二进制存储格式来在外存上存储,换句话说就是文件在外存的存储格式和数据在内存中的存储格式一致,使用二进制文件存储数据就是直接将数据在内存占用的内容直接拷贝到外存中。无需任何格式转化。 1、 可保存任意类型的数据。文本文件只存储字符类型数据,任何其它类型 的数据必须转换成字符串才能保存到文本文件中。而二进制文件可保存 任意类型的数据 2、存储效率高。和文本文件相比,将内存变量中数据保存成二进制文件的 效率更高。效率高具体表现在两个方面:一是无需要格式转换,保存速度快;二是保存数值型数据所占用的存储空间少。例如,保存1个short型整数-2100,使用二进制文件需要2个字节。使用文本文件,需将short型 整数-2100转换成字符串”-2100”,保存该字符串需要5个字节 (分开来存的) 3、 通用性差。二进制文件是程序员自己定义的一种私有格式。不同程序会 创建不同的二进制文件。不管什么类型的数据,保存到二进制文件后都变成了二进制的0、1序列。在不了解存储格式的情况下,任何程序都无法正确解释由其它程序创建的二进制文件。二进制文件天生就是一种加密的文件。和文本文件相比,二进制文件的通用性差。对于二进制数据文件,通常是由哪个程序创建,就由哪个程序负责阅读、修改 4、交换数据需遵循相同的格式标准。为了能在不同程序间通过二进制文件交换数据,人们需要为二进制文件制定共同的格式标准。例如为了交换图像数据,人们专门制定了一些二进制图像文件的格式标准,常用的有 JPEG、BMP、GIF、TIFF – 打开文件 程序在对文件进行输入/输出操作之前,首先需要打开文件。打开(Open)文件时需指定文件名和打开模式。打开模式指的是程序将对文件进行何种操作,例如读数据(输入)、写数据(输出),或是两者都有(输入+ 输出)等 – 读/写数据 文件打开以后,程序可以从文件中读数据(输入)、或向文件中写数据 (输出),或是两者都有(输入+输出)。向文件读/写数据可以按存储顺序依次读/写,这称为顺序读/写。也可以指定从某个位置开始读/写,这称为随机读/写 – 关闭文件 程序在完成对文件的输入/输出操作之后,需要关闭文件。通常情况下, 数据文件只能被一个程序打开,这样可以避免读/写冲突。程序在关闭数 据文件之后,该文件才可以再被其它程序打开。 C++流类库中定义了3个不同的文件输入/输出流类 1、 文件输出流类ofstream 2、文件输入流类ifstream 3、以及文件输入/输出流类fstream 定义文件输入/输出流类的对象,通过对象调用其函 数成员就可以实现文件的输入/输出功能。文件输入 /输出流类的对象被称为是文件对象。使用文件输入/输出流类需包含类声明头文件: #include 下面是文件输出流类ofstream的示意代码 ofstream新增函数:open(),is_open(),close()还有哦stream类中的函数put()等等(详细请看目录通用输出流类ostream:) 例子1文本文件输出: 例子2二进制文件输出: 下面是文件输入流类ifstream的示意代码: 例子1文本文件输入: 例子2二进制文件输入: – eof( )。检查文件是否已结束 – good( )。检查文件是否已损坏。具体用法: 下面为文件输入/输出流类fstream的示意代码: 文件输入输出流类fstream包括: – 从类istream继承的基类成员。其中包括提取运算符“>>”, 函数成员get、getline、read、seekg和tellg等。这些成员是从 类iostream间接继承来的,它们提供了文件输入功能 – 从类ostream继承的基类成员。其中包括插入运算符“<<”, 函数成员put、write、seekp、tellp和flush等。这些成员也从 类iostream间接继承而来,它们提供了文件输出功能 – 新增函数成员。其中包括打开文件函数open和关闭文件函数 close等 建立文件对象与外存某个文件的关系,称为打开文件。 函数原型:打开文件函数open void open( const char * filename, int open_mode); open_mode为打开方式(是ios基类定义的枚举变量): 举例 //以不同模式打开文件“aaa.dat”的举例 – 文件输入流对象包含一个读文件指针 – 文件输出流对象包含一个写文件指针 通常打开文件时文件对象的读写指针都定位与文件头的位置,每执行一次,读写指针都将自动后移,移动到下一个读写的位置。移动和查找读写指针对应有四个指针: 例子1: #include
流错误状态函数和状态字
状态字
该函数返回 failbit状态值,用于判断流操作是否失败。 failbit表示发生流格式错误,但缓冲
区中的字符没有丢失。这种错误通常是可以修复的。
3、int good() const;
被设置,则返回1(true)否则返回0(false)
正常。例如: cin.clear():将清除cin的状态字,并设置为 goodbit。而cin.clear(ios::failbit);
给流设置了 failbit。当程序操作遇到某些问题时可能需要这样做,在问题解决后再恢复。
格式控制
iostream和 iomanip类还提供了一批控制符以简化I/O格式化的操作,这些篇幅很长,不在此一一介绍了,列举常用格式控制如下:#include
#include
#include
文件I/O
文件输入/输出流类
文件输出流类ofstream
class ofstream : public ostream // 公有继承通用输出类ostream
{
public:
ofstream(); // 无参构造函数
ofstream(const char *, int =ios::out); // 有参构造函数
~ofstream(); // 析构函数
void open(const char *, int =ios::out); // 打开文件
bool is_open() const; // 检查文件是否正确打开
void close(); // 关闭文件
// ...... 以下代码省略
};
// 输出文本文件
#include
#include
文件输入流类ifstream
class ifstream : public istream // 公有继承通用输入类istream
{
public:
ifstream(); // 无参构造函数
ifstream(const char*, int = ios::in); // 有参构造函数
~ifstream(); // 析构函数
void open(const char*, int = ios::in); // 打开文件
bool is_open() const; // 检查文件是否正确打开
void close(); // 关闭文件
// ...... 以下代码省略
};
#include
#include
检查输入文件状态
#include
文件输入输出流类fstream
class iostream : public istream, public ostream // 多继承类istream和ostream
{
public:
virtual ~iostream(); // 虚析构函数
protected:
iostream(); // 无参构造函数
iostream(const iostream&); // 有参构造函数
// ...... 以下代码省略
};
class fstream : public iostream // 公有继承通用输入/输出类iostream
{
public:
fstream(); // 无参构造函数
fstream(const char*, int); // 有参构造函数
~fstream(); // 析构函数
void open(const char*, int); // 打开文件
bool is_open() const; // 检查文件是否正确打开
void close(); // 关闭文件
// ...... 以下代码省略
};
fstream fobj; // 定义1个fstream类的文件对象fobj
fobj.open("aaa.dat", ios::in); // 以输入/文本模式打开文件“aaa.dat”,默认为文本模式
fobj.open("aaa.dat", ios::in | ios::binary); // 以输入/二进制模式打开文件“aaa.dat”
fobj.open("aaa.dat", ios::out | ios::binary); // 以输出/二进制模式打开文件“aaa.dat”
fobj.open("aaa.dat", ios::out | ios::app); // 以输出/追加/文本模式打开文件“aaa.dat”
fobj.open("aaa.dat", ios::out | ios::trunc); // 以输出/清空/文本模式打开文件“aaa.dat”
文件的随机读写
istream& seekg( long bytes, ios::seek_dir origin ); // 移动读文件指针
//bytes正数表示向后移动,负数则向前移动,origin表示指针移动的起始位置
long tellg( ); // 返回当前读文件指针的位置
ostream& seekp( long bytes, ios::seek_dir origin ); // 移动写文件指针
//bytes正数表示向后移动,负数则向前移动
long tellp(); // 返回当前写文件指针的位置,origin表示指针移动的起始位置
#include



