赞
踩
c语言的标准输入输出库是stdio.h 是一个函数库而不是类库。
其中包括了我们最常使用的scanf printf 都是一些独立的全局函数,因为C语言是不支持类的。
c++的标准输入输出库iostream 是一个类库,以类的形式组织,使用该库中的类要先引用命名空间:using namespace std;
最常使用的是cin和cout,这两个都是对象,cin是istream类的对象,cout是ostream类的对象,而输入的cin>>与输出时的cout<<中的左移<<与右移>>分别是istream类与ostream类的操作符重载。
iostream库里面创建了3个标准流对象:
1 cin 表示标准输入的istream对象,cin可以使我们从设备读取数据。
2 cout 表示标准输出的ostream对象,cout可以使我们向设备写入数据。
3 cerr 表示标准错误的ostream对象,cerr是导出程序错误消息的地方,只能向屏幕设备写数据。
标准的流对象都有默认的设备:
cout << data; cout默认的设备是显示器缓冲区。
cin >> data; cin默认的设备是键盘缓冲区。
iostream库由以下几个库组成:fstream, iomainip, ios, iosfwd, iostream, istream, ostream, sstream, streambuf, strstream。
istream用来访问操作系统的输入流,ostream访问操作系统的输出流,iostream同时继承了这两个类。
在ostream类中,重载了许多的左移<<操作符,对每种基本数据类型都做了重载,比如
&ostream operator<<(ostream &temp, int source);
&ostream operator<<(ostream &temp, char source);
&ostream operator<<(ostream &temp, char* source);
由于以上重载返回的数据类型都是ostream的引用,ostream又可以作为左值,所以可以实现cout<<"abc"<<endl<<123;
同样在istream类中,也重载了许多右移>>操作符,对每种基本数据类型都做了重载,比如
&istream operator>>(istream &temp,int source);
&istream operator>>(istream &temp,char source);
以上是终端标准输入输出设备的输入输出,也就是一般pc机的键盘和显示器的输入输出。
http://www.cnblogs.com/coderlee/archive/2008/01/21/1046928.html
iostream库不仅支持终端设备的输入输出,还支持文件的输入输出,和文件有关的输入输出类声明在fstream头文件中,有三个类负责文件的输入输出
1) ifstream类:从istream类派生。
2) ofstream类:从ostream类派生。
3) fstream类:从iostream类派生。
由于文件的输入输出和键盘鼠标的输入输出是不一样的,一般pc机只有一个键盘设备,所以iostream库内部声明了一个istream类的对象cin,这个对象负责从键盘获取数据,而文件设备在系统中是由许多的,所以iostream库内部无法给你为机器的每个文件都创建一个负责获取数据的ifstream对象和负责写入数据的ofstream对象,所以我们要针对一个文件进行读取或写入数据的时候都要自己创建一个ifstream或ostream类的对象来用。
ofstream类的默认构造函数如下:
ofstream::ofstream(const char* filename, int mode = ios::out, int openport = filebuf::openport);
filename是要打开的文件名,
mode是打开的方式,
openport是打开文件的属性。
mode可以设置的方式如下:
ios::app 以追加的方式打开
ios::ate 文件打开后定位到文件尾
ios::binary 以二进制方式打开文件,默认是以文本方式打开
ios::in 文件以读(输入)方式打开
ios::out 文件以写(输出)方式打开
ios::trunc 如果文件存在,则把文件清空。
以上属性用“|”(按位或)连接起来。
openprot属性如下:
0 普通文件
1 只读文件
2 隐含文件
4 系统文件
以上属性可以用加或者按位或方式组织起来,比如1|2和3都代表既是只读又是隐含文件。
在windows操作系统中可以不要第三个参数,如果加入第三个参数,那第三个参数是打开文件的共享方式,也就是打开这个文件时,其他进程是否可以读写该文件。
共享方式参数可以是下面的值:
0x10 //_SH_DENYRW Denies read and write access to the file
0x20 //_SH_DENYWR Denies write access to the file
0x30 //_SH_DENYRD Denies read access to the file.
0x40 //_SH_DENYNO Permits read and write access
其他值都会报 "Invalid sharing flag "的错误。
- ofstream hFile("c:\\1.txt", ios::out, _SH_DENYRW); //_SH_DENYRW is deny read and write
- if(!hFile) // if the file could open, hFile is a handle, else is zero
- {
- cout << "write fail!" << endl;
- cout << "access is denies,maybe the file is readonlys,or use deny read opened of other process." << endl;
- }
- else
- {
- hFile << "by coderlee writes";
- cout << "write success!" << endl;
- }
- hFile.close(); // opened file need close.
上面是写文件的事例代码,先打开文件,然后判断是不是0,如果是0,则提示write fail否则写文件,提示write success.
http://www.cnblogs.com/coderlee/archive/2008/01/21/1046932.html
ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间;
在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
1、插入器(<< )
向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout< < " Write Stdout" < < '/n'; 就表示把字符串" Write Stdout" 和换行字符('/n')输出到标准输出流。
2、析取器(>> )
从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin> > x; 就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。
一、打开文件
在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
void open(const char* filename,int mode,int access);
参数:
filename: 要打开的文件名
mode: 要打开文件的方式
access: 打开文件的属性
打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:
ios::app: 以追加的方式打开文件
ios::ate: 文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary: 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in: 文件以输入方式打开(文件数据输入到内存)
ios::out: 文件以输出方式打开(内存数据输出到文件)
ios::nocreate: 不建立文件,所以文件不存在时打开失败
ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败
ios::trunc: 如果文件存在,把文件长度设为0
可以用“或”把以上属性连接起来,如ios::out|ios::binary
打开文件的属性取值是:
0:普通文件,打开访问
1:只读文件
2:隐含文件
4:系统文件
可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。
例如:以二进制输入方式打开文件c:/config.sys
- fstream file1;
- file1.open("c://config.sys", ios::binary|ios::in,0);
如果open函数只有文件名一个参数,则是以读/写普通文件打开,即:
file1.open("c://config.sys" ); < => file1.open(" c://config.sys" ,ios::in|ios::out,0);
另外,fstream还有和open()一样的构造函数,对于上例,在定义的时侯就可以打开文件了:
fstream file1(" c://config.sys" );
特别提出的是,fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式打开文件,而ofstream默认以输出方式打开文件。
- ifstream file2(" c://pdos.def" ); //以输入方式打开文件
- ofstream file3(" c://x.123" ); //以输出方式打开文件
所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。
二、关闭文件
打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close(); 就把file1相连的文件关闭。
三、读写文件
读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式
1、文本文件的读写
文本文件的读写很简单:用插入器(<< )向文件输出;用析取器(>> )从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:
- file2<< " I Love You" ; //向文件写入字符串" I Love You"
- int i;
- file1>> i; //从文件输入一个整数值。
这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
操纵符 功能 输入/输出
dec 格式化为十进制数值数据 输入和输出
endl 输出一个换行符并刷新此流 输出
ends 输出一个空字符 输出
hex 格式化为十六进制数值数据 输入和输出
oct 格式化为八进制数值数据 输入和输出
setpxecision(int p) 设置浮点数的精度位数 输出
比如要把123当作十六进制输出:file1< < hex< < 123; 要把3.1415926以5位精度输出:file1< < setpxecision(5)< < 3.1415926。
2、二进制文件的读写
①put()
put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put('c'); 就是向流写一个字符'c'。
②get()
get()函数比较灵活,有3种常用的重载形式:
一种就是和put()对应的形式:ifstream &get(char & ch); 功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x); 表示从文件中读取一个字符,并把读取的字符保存在x中。
另一种重载形式的原型是: int get(); 这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get(); 和上例功能是一样的。
还有一种形式的原型是:ifstream &get(char *buf,int num,char delim='/n');这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符'/n'。例如:
file2.get(str1,127,'A'); //从文件中读取字符到字符串str1,当遇到字符'A'或读取了127个字符时终止。
③读写数据块
要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:
- read(unsigned char *buf,int num);
- write(const unsigned char *buf,int num);
read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount(); 来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
例:
- unsigned char str1[]=" I Love You" ;
- int n[5];
- ifstream in("xxx.xxx");
- ofstream out("yyy.yyy");
- out.write(str1,strlen(str1)); //把字符串str1全部写到yyy.yyy中
- in.read((unsigned char*)n,sizeof(n)); //从xxx.xxx中读取指定个整数,注意类型转换
- in.close();
- out.close();
四、检测EOF
成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
例:
- if(in.eof()){
- ShowMessage("已经到达文件尾!");
- }
五、文件定位
和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是seekg()和seekp()。seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
- istream &seekg(streamoff offset,seek_dir origin);
- ostream &seekp(streamoff offset,seek_dir origin);
streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
ios::beg: 文件开头
ios::cur: 文件当前位置
ios::end: 文件结尾
这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。例:
- file1.seekg(1234,ios::cur); //把文件的读指针从当前位置向后移1234个字节
- file2.seekp(1234,ios::beg); //把文件的写指针从文件开头向后移1234个字节
http://hi.baidu.com/luckymouse2009/blog/item/80042808ee129da62fddd47e.html
写一个在一个流中同时读写文件的小程序
- string login , user;
- fstream logfile("log.dat",fstream::in | fstream::out | fstream::trunc);
- if (!logfile)
- {
- exit(-1);
- }
- logfile << "danny is pig" << endl; // 写一条新记录
- logfile << "you are right" << endl;
- // int off = logfile.tellp();
- logfile.seekp(ios::beg); // 位置重置
- logfile >> login >> user; // 读取以前写入的值
-
- cout << login << user << endl;
- // logfile.seekp(off,ios::beg);
- for (int i = 0 ; i < 10; i++)
- {
- logfile << "you are dog" << endl;
- }
刚开始没有写被注释掉的那两句,结果“you are dog”怎么也写不到文件里去。估计文件指针不知道跑到哪个地方去了。加了这两句在写之前恢复一下先前写的位置,再写就可以了,具体指针跑到哪了不是很清楚,有知道内部细节的一定要告诉我哦。
下面同时摘录了网上关于这方面的一点小知识
本文主要讨论C++标准I/O库,主要内容为控制台输入输出流、文件流、字符串流。
如果文中有错误或遗漏之处,敬请指出,谢谢!
流介绍
标准I/O类的头文件
<iostream> 包含istream、ostream、iostream这三个类。其中,iostream由istream和ostream派生而来。
<fstream> 包含ifstream、ofstream、fstream这三个类。其中,ifstream由istream派生,ofstream由ostream派生,fstream由iostream派生。
<sstream> 包含istringstream、ostringstream、stringstream这三个类。其中,istringstream由istream派生,ostringstream由ostream派生,stringstream由iostream派生。
注意:标准库I/O对象不允许拷贝或赋值。
C++标准库的流类完整关系图如下:(注意:我发现一个问题,下面的图在打开网页时有时显示模糊,显示的图比实际的小,有两种方法:一是多刷新几次就能刷出来;二是另外显示该图片^_^)
其中,typedef basic_ios<char, char_traits<char> > ios;
typedef basic_istream<char, char_traits<char> > istream;
typedef basic_ostream<char, char_traits<char> > ostream;
typedef basic_iostream<char, char_traits<char> > iostream;
typedef basic_istringstream<char> istringstream;
typedef basic_ostringstream<char> ostringstream;
typedef basic_stringstream<char> stringstream;
流状态
ios_base::iostate 机器相关的整型类型名,用于定义流状态。I/O标准库的基类ios中定义了四个为静态成员的该类型的标志位:
ios_base::badbit 该标志位表示该I/O流发生了严重错误(常指物理破坏,没法修正)
ios_base::eofbit 该标志位表示该I/O流已经到达文件尾
ios_base::failbit 该标志位表示该I/O流出现了失败的I/O操作
ios_base::goodbit 该标志位表示该I/O流状态正常
注意:各标志位可以用|运算符连接起来;为了方便,可以用ios代替ios_base来使用这些标志位(标志位是ios_base的静态常量成员),后面内容中出现的ios_base也可以这样。
检测流状态:
ios::bad() 如果流的badbit标志位被设置,则返回true;否则返回false
ios::eof() 如果流的eofbit标志位被设置,则返回true;否则返回false。如果eofbit被设置,那么failbit也将被设置。
ios::fail() 如果流的failbit标志位被设置,则返回true;否则返回false
ios::good() 如果流的goodbit标志位被设置,则返回true;否则返回false。仅当bad(),eof(),fail()都返回 false时,good()才返回true。
除了可以直接调用流的这些成员函数进行流状态检测外,很多时候我们还可以把流对象直接用于布尔表达式中进行测试。例如:
int i;
while (cin >> i)
cout << i << endl;
在这种情况下,流到void*类型的转换operator void*()被隐式调用,然后再隐式转换为bool型。
为什么流类不直接定义一个到bool类型的隐式转换呢?这是因为这样做可能会引起错误。因为bool型可以隐式转换到int型,从而使流类对象可以隐式转换到int型,这样极容易引用错误。例如,原本打算输入“cin >> i”,但实际上却输入了“cin > i”,漏掉了一个“>”,如果有流对象到bool的隐式转换,那么“cin > i”语法上就是正确的,相当于最终两个int型的比较。显然,我们不希望这种情况下发生。另外,由于历史原因,bool型在最先的C++中是不存在的,所以转换到了void*类型。
设置/获取流状态:
ios_base::clear(state) 将流s标志位设置为state,默认参数为goodbit
ios_base::setstate(state) 为流s添加指定的标志位state,原来的标志位仍然存在,相当于调用clear(state | rdstate())
ios_base::rdstate() 返回流s的当前标志位
输出缓冲区的管理
下面几种情况将导致输出缓冲区中的内容被刷新到输出设备或文件:
1) 程序正常结束。作为main返回工作的一部分,将清空所有输出缓冲区;
2) 在一些不确定的时候,缓冲区可能已经满了。在这种情况下,缓冲区将会在写下一个值之前刷新;
3) 用操作符(manipulator)显式地刷新缓冲区,例如 endl,flush等;
4) 在每次输出操作执行后,用unitbuf操纵符设置流的内部状态,从而清空缓冲区;
5) 可将输出流与输入流关联(tie) 起来。在这种情况下,在读输入流时将刷新其关联的输出流缓冲区。
下面列举用于刷新缓冲区的操纵符:
endl操纵符 输出一个换行符,并刷新缓冲区
ends操纵符 输出一个空字符null,然后刷新缓冲区
flush操纵符 直接刷新缓冲区,不添加任何字符
unitbuf操纵符 它在每次执行完写操作后都刷新缓冲区
例如:
cout<<unitbuf<<"first"<<" second"<<nounitbuf;
等价于:
cout<<"first"<<flush<<" second"<<flush;
其中,nounitbuf操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。
将输入和输出关联起来,用tie函数实现:
basic_ostream<E, T> *tie() const;
basic_ostream<E, T> *tie(basic_ostream<E, T> *str);
tie函数可以由istream或ostream对象调用,使用一个指向ostream对象的指针形参。第一个函数返回上次存储的被关联的ostream对象指针,第二个函数设置新的关联对象,并返回上次关联的对象的指针。如果第二个函数的参数为0,则断开两个对象之间的关联。例如:
- cin.tie(&cout); // tie cin and cout
- ostream* old_tie = cin.tie();
- cin.tie(0); // break tie between cin and cout
- cin.tie(&err); // a new tie
- cin.tie(old_tie); // restablish tie between cin and cout
文件流
如果文件流已经与一个指定的文件相关联,若要把该文件流与另一个文件关联,则必须先关闭(close)现在的文件,再打开(open)另一个文件。open函数会检查是否已经有文件被打开;如果有,则设置failbit标志位,以指示错误,并且此时对文件流的任何读写操作都会失败。此时,可以清除该标志,然后可以继续对前面的文件进行操作。
文件模式
ios_base::in 读模式
ios_base::out 写模式
ios_base::app 追加模式
ios_base::ate 打开文件后立即定位到文件尾
ios_base::trunc 打开文件时清空文件内容(如果有的话)
ios_base::binary 以二进制模式打开文件
上面的标志位被声明为类ios_base的静态常量成员。其中,out、truc、app模式只能用于与ofstreamt和fstream对象关联的文件;in模式只能用于与ifstreamt和fstream对象关联的文件;ate和binary模式可以用于所有文件。
如果以binary模式打开文件,则文件流将以字节序列处理文件内容,不会对内容作任何解释;否则,默认用文本模式打开文件,不同的系统可能会对文件内容作一些解释转换。比如,在非UNIX系统如Windows系统中,换行符\n会被解释成回车换行\r\n到文件系统,\r\n会被解释成\n到内存。而在UNIX系统中,二进制模式和文本模式是没有区别的。
默认情况下,与ifstream流对象关联的文件将以in模式打开;与ofstream关联的文件则以out模式打开,并且以out模式打开的文件的内容会被清空(相当于同时也指定了trunc模式);与fstream关联的文件则以in和out模式打开。
如果打开与fstream关联的文件时,只用了out模式,而不指定in模式,则文件内容会被清空;如果指定了trunc模式,不论是否指定in模式,文件内容都会被清空。
文件模式的有效组合
out 打开文件进行写操作,删除文件中已有数据;如果文件不存在,则创建文件。
app 打开文件进行写操作,在文件尾追加数据;若文件是与ofstream流关联,那么,当文件不存在时创建文件;若是与fstream流关联,那么,当文件不存在时操作失败。
out | app 追加数据;如果文件不存在,则创建文件。
out | trunc 与out模式相同
in 打开文件进行读操作
in | out 打开文件进行读、写操作,并定位于文件形头处
in | out | trunc 打开文件进行读、写操作,并且删除文件中已有数据
字符串流
字符串流(sstream)定义了一个以string对象为形参的构造函数。对sstream对象的读写操作实际上是对该对象中的string对象进行操作。
sstream类还定义了一个名为str()的成员,用来读取或设置sstream对象所操纵的string对象。
- basic_string<E, T, A> str() const;
- void str(basic_string<E, T, A>& x);
字符串流的通常用法
用来实现格式化数据输入输出,相当于C语言中的fscanf和fprintf函数的功能。例如:
- int var1 = 5, var2 = 10;
- stringstream ss;
- ss<<"Var1: "<<var1<<" Var2: "<<var2;
- cout<<"SS<<: "<<ss.str()<<endl;
- var1 = var2 = 0;
- string dump;
- ss>>dump>>var1>>dump>>var2;
- cout<<"SS>>: Var1: "<<var1<<", Var2: "<<var2<<endl;
--------------------------------------------------------------------------------
流的格式化I/O
操纵符
<iostream>中定义的操纵符(带*号的表示是默认使用的)
boolalpha 将真和假显示为字符串
*noboolalpha 将真和假显示为1,0
showbase 产生数的基数前缀
*noshowbase 不产生数的基数前缀
showpoint 总是显示小数噗
*noshowpoint 有小数部分才显示小数点
showpos 显示非负数中的+(0也显示+)
*noshowpos 不显示非负数中的+
uppercase 在十六进制中打印0X,科学记数法中打印E
*nouppercase 在十六进制中打印0x,科学记数法中打印e
*dec 用十进制显示
hex 用十六进制显示
oct 用八进制显示
left 在对齐,在值的右边增加填充字符
*right 右对齐,在值的左边增加填充字符
internal 两边对齐,在值和符号之间填充字符
fixed 用小数形式显示浮点数
scientific 用科学记数法显示浮点数
flush 刷新ostream缓冲区
ends 插入空字符,然后刷新ostream缓冲区
endl 插入换行符,然后刷新ostream缓冲区
unitbuf 在每个输出操作之后刷新缓冲区
*nounitbuf 恢复常规缓冲区刷新
*skipws 为输入操作符>>跳过空白字符(空格、制表位、换行)
noskipws 不为输入操作符跳过空白
ws “吃掉”空白
默认情况下,用于显示浮点数的记数法取决于数的大小:如果数很大或者很少,将按科学记数法显示;否则,使用固定位数的小数显示。
如果设置了scientific或者fixed,由于不存在相应的操纵符来恢复默认值,只能通过调用unsetf成员来取消scientific或fixed所做的改变:unsetf(ios_base::floatfield);(函数原型:void unsetf(fmtflags mask);)
<iomanip>中定义的操纵符
setfill(char ch) 设置ch为空白填充字符
setprecision(int n) 将浮点精度设置为n
setw(int w) 读写w个字符的值
setbase(int b) 按基数b输出整数,n取值为8、10、16;若为其它值,则基数为10
setiosflags(ios_base::fmtflags n) 相当于调用setf(n)
resetiosflags(ios_base::fmtflags n) 清除由n代表的格式化标志
默认情况下,精度指定数字的总位数(小数点之前和之后),默认为6位。使用fixed或者scientific之后,精度指小数点之后的位数。
setw不改变流状态,只对后面的一个数据输出有效。其它操纵符改变流格式状态后,I/O流保留改变后的格式状态。
flag成员函数
ios类提供了flag成员函数用来设置和恢复流的格式状态(所有标志位),函数原型如下:
ios_base::fmtflags flags() const; // 返回流的当前格式状态
ios_base::fmtflags flags(ios_base::fmtflags fmtfl); // 设置流的格式状态
setf和unsetf成员函数
ios类提供了这两个成员函数用来设置或者取消某个标志位,其函数原型如下:
void setf(ios_base::fmtflags mask);
ios_base::fmtflags setf(ios_base::fmtflags fmtfl, fmtflags mask);
void unsetf(ios_base::fmtflags mask); // 清除指定标志位
其中,第一个setf版本适用于开关标志位,这些开关标志位有:
ios_base::boolalpha, ios_base::skipws, ios_base::showbase, ios_base::showpoint, ios_base::uppercase, ios_base::showpos, ios_base::unitbuf
第二个setf版本适用于格式化域标志位,一次只能设置这些标志中的一个。这些格式化域及其标志位如下:
ios_base::basefield: ios_base::dec, ios_base::hex, ios_base::oct
ios_base::floatfield: ios_base::scientific, ios_base::fixed
ios_base::adjustfield: ios_base::left, ios_base::right, ios_base::internal
例如,
s.setf(ios_base::boolalpha); // 设置布尔值显示为字符形式
s.seft(ios_base::scientific, ios_base::floatfield); // 设置小数输出形式为科学记数法
s.unsetf(ios_base::floatfield); // 清除floatfield域的标志位
其它成员函数
ios类还提供了其它几个成员函数来控制输出域格式,其函数原型如下:
int ios_base::width(); // 返回当前宽度,默认为0
int ios_base::width(int n); // 设置宽度
char ios::fill();
char ios::fill(char n);
int ios_base::precision();
int ios_base::precision(int n);
--------------------------------------------------------------------------------
流的未格式化的输入输出操作
单字节操作
is.get(ch) 将istream is的下一个字节放入字符ch中,返回is
os.put(ch) 将字节ch放入ostream os中,返回os
is.get() 返回is的下一字节作为一个int值
is.putback(ch) 将字符ch放回is,返回is
is.unget() 将is退回一个字节,返回is
is.peek() 将下一字节作为int值返回但不移出它(不移动流缓冲区指针)
一般而言,保证能够在下一次读之前放回最多一个值,也就是说,不保证能够连续调用putback或unget而恢复原来的流状态。
为什么get()和peek()要返回int型,而不是char型呢?原因是为了允许返回一个文件结束符。由于允许给定字符集使用char范围的每一个值来表示实际字符,因此,该范围中没有额外值用来表示文件结束符。相反,这些函数把字符转换为unsigned char,然后将那个值提升为int,因此,即使字符集有映射到负值的字符,从这些操作返回的值也将是一个正值。通过将文件结束符作为负值返回,标准库将保证文件结束符区别于任意合法字符值。文件结束符EOF定义于<iostream>文件中,为一个为负值的const变量,用它来标志文件是否结束。例如:
- int ch; // int, not char!
- while ((ch = cin.get()) != EOF)
- cout.put(ch);
多字节操作
is.get(buf, size, delim),其函数原型为:
basic_istream& get(E *s, streamsize n, E delim = '\n'); 从is中读入size个字节并将它们存储到buf所指向的空间中,返回is。当读操作遇到delim字符、或者文件结束符、或者已经读入了size个字节,那么读操作结束。如果遇到delim,它将被留在输入流中。
is.getline(buf, size, delim),其函数原型为:
basic_istream& getline(E *s, streamsize n, E delim = '\n'); 与上面的get行为相似,区别是读取并丢弃delim。
is.read(buf, size) 读取size个字节到buf中,返回is
is.gcount() 返回最后一个未格式化读操作从流is中读到的字节数
os.write(buf, size) 将size个字节从数组buf写到os,返回os
is.ignore(size, delim) 读并忽略size个字符,直到遇到delim,但不包括delim。size的默认参数为1,delim默认参数为文件结束符。
由于将字符放回流中的单字符操作也是未格式化输入操作,如果在调用gcount之前调用了peek、unget或者putback,则返回值是0。
举例:当输入缓冲区发生错误时,需要清空缓冲区时,可以这样做:
- if (!cin) {
- cin.clear();
- cin.ignore(numeric_limits<int>::max(), '\n');
- }
另外,需要注意的是使用低级I/O操作容易出错,提倡使用标准库的高级抽象。例如,返回int值的I/O操作就是一个很好的例子。
将get或其它返回int值的函数的返回值赋给char对象而不是int对象,是一个常见的错误。至于这种错误,具体在机器上发生什么行为,取决于机器和输入数据。例如,在将char实现为unsigned char的机器上,这是一个死循环:
- char ch;
- while ((ch = cin.get()) != EOF){
- cou.put(ch);
- }
这是因为,当get返回EOF的时候,那个值将被转换为unsigned char值,转换后的值不再等于EOF的整型值,形成死循环。
--------------------------------------------------------------------------------
流的随机访问
I/O流提供了两个成员函数来实现随机访问:定位函数(seek)和查询函数(tell)。如下表所示:
seekg 重新定位输入流中的读指针(g = get)
tellg 返回输入流中读指针的当前位置(g = get)
seekp 重新定位输出流中的写指针(g = put)
tellp 返回输出流中写指针的当前位置 (g = put)
注意:在每个流中(即使是可同时输入输出的流,如fstream),只有一个读(写)指针,标准库将g位置和p位置都映射到这个指针。所以,在读和写之间切换时,必须进行seek来重新定位标记。
另外,由于istream和ostream类型一般不支持随机访问,所以,流的随机访问只适用于fstream和sstream类型。
seekg和seekp均有两个重载版本,一个使用绝对地址,另一个使用相对偏移。其函数原型如下:
- basic_istream& seekg(pos_type pos);
- basic_istream& seekg(off_type off, ios_base::seek_dir way);
- basic_ostream& seekp(pos_type pos);
- basic_ostream& seekp(off_type off, ios_base::seek_dir way);
在上面的函数中,way参数有几个预定义取值:
ios_base::beg 表示流的开头
ios_base::cur 表示流的当前位置
ios_base::end 表示流的末尾
另外,seek_dir、pos_type和off_type均是ios_base类里面的类型。
下面是两个tell的函数原型:
pos_type tellg()
pos_type tellp();
--------------------------------------------------------------------------------
流类和异常
除了手工检查流状态外,还可以利用异常机制来解决流类错误问题。流的成员函数exceptions()接受一个参数,这个参数用于表示程序员希望在哪个流状态标志位出现时抛出异常 。当流遇到这样的状态时,就抛出一个std::ios_base::failure类型的异常,其继承自std::exception。函数原型如下:
iostate exceptions() const;
iostate exceptions(iostate except);
-----------------------------------------------------------------------------
如果文中有错误或遗漏之处,敬请指出,谢谢!
------------------------------------------------------------------------------
参考文献:
[1] C++ Primer(Edition 4)
[2] Thinking in C++(Volume Two, Edition 2)
[3] International Standard:ISO/IEC 14882:1998
http://blog.csdn.net/wh0826/article/details/5380644
C++ 通过以下几个类支持文件的输入输出:
ofstream: 写操作(输出)的文件类 (由ostream引申而来)
ifstream: 读操作(输入)的文件类(由istream引申而来)
fstream: 可同时读写操作的文件类 (由iostream引申而来)
打开文件(Open a file)
对这些类的一个对象所做的第一个操作通常就是将它和一个真正的文件联系起来,也就是说打开一个文件。被打开的文件在程序中由一个流对象(stream object)来表示 (这些类的一个实例) ,而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。
要通过一个流对象打开一个文件,我们使用它的成员函数open():
void open (const char * filename, openmode mode);
这里filename 是一个字符串,代表要打开的文件名,mode 是以下标志符的一个组合:
ios::in 为输入(读)而打开文件
ios::out 为输出(写)而打开文件
ios::ate 初始位置:文件尾
ios::app 所有输出附加在文件末尾
ios::trunc 如果文件已存在则先删除该文件
ios::binary 二进制方式
这些标识符可以被组合使用,中间以”或”操作符(|)间隔。例如,如果我们想要以二进制方式打开文件"example.bin" 来写入一些数据,我们可以通过以下方式调用成员函数open()来实现:
ofstream file;
file.open ("example.bin", ios::out | ios::app | ios::binary);
ofstream, ifstream 和 fstream所有这些类的成员函数open 都包含了一个默认打开文件的方式,这三个类的默认方式各不相同:
类 参数的默认方式
ofstream ios::out | ios::trunc
ifstream ios::in
fstream ios::in | ios::out
只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合。
由于对类ofstream, ifstream 和 fstream 的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open 函数,并拥有同样的参数。这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:
ofstream file ("example.bin", ios::out | ios::app | ios::binary);
两种打开文件的方式都是正确的。
你可以通过调用成员函数is_open()来检查一个文件是否已经被顺利的打开了:
bool is_open();
它返回一个布尔(bool)值,为真(true)代表文件已经被顺利打开,假( false )则相反。
关闭文件(Closing a file)
当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:
void close ();
这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。
为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close。
文本文件(Text mode files)
类ofstream, ifstream 和fstream 是分别从ostream, istream 和iostream 中引申而来的。这就是为什么 fstream 的对象可以使用其父类的成员来访问数据。
一般来说,我们将使用这些类与同控制台(console)交互同样的成员函数(cin 和 cout)来进行输入输出。如下面的例题所示,我们使用重载的插入操作符<<:
- // writing on a text file
- #include <fiostream.h>
- int main () {
- ofstream examplefile ("example.txt");
- if (examplefile.is_open()) {
- examplefile << "This is a line."n";
- examplefile << "This is another line."n";
- examplefile.close();
- }
- return 0;
- }
- file example.txt
-
- This is a line.
- This is another line.
从文件中读入数据也可以用与 cin的使用同样的方法:
- // reading a text file
- #include <iostream.h>
- #include <fstream.h>
- #include <stdlib.h>
-
- int main () {
- char buffer[256];
- ifstream examplefile ("example.txt");
- if (! examplefile.is_open())
- {
- cout << "Error opening file";
- exit (1);
- }
- while (! examplefile.eof() ) {
- examplefile.getline (buffer,100);
- cout << buffer << endl;
- }
- return 0;
- }
- This is a line.
-
- This is another line.
上面的例子读入一个文本文件的内容,然后将它打印到屏幕上。注意我们使用了一个新的成员函数叫做eof ,它是ifstream 从类 ios 中继承过来的,当到达文件末尾时返回true 。
状态标志符的验证(Verification of state flags)
除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):
bad()
如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
fail()
除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。
eof()
如果读文件到达文件末尾,返回true。
good()
这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false 。
要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。
获得和设置流指针(get and put stream pointers)
所有输入/输出流对象(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 从流末尾处开始计算的位移
流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。
以下例子使用这些函数来获得一个二进制文件的大小:
- // obtaining file size
- #include <iostream.h>
- #include <fstream.h>
- const char * filename = "example.txt";
- int main () {
- long l,m;
- ifstream file (filename, ios::in|ios::binary);
- l = file.tellg();
- file.seekg (0, ios::end);
- m = file.tellg();
- file.close();
- cout << "size of " << filename;
- cout << " is " << (m-l) << " bytes."n";
- return 0;
- }
size of example.txt is 40 bytes.
二进制文件(Binary files)
在二进制文件中,使用<< 和>>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。
文件流包括两个为顺序读写数据特殊设计的成员函数:write 和 read。第一个函数 (write) 是ostream 的一个成员函数,都是被ofstream所继承。而read 是istream 的一个成员函数,被ifstream 所继承。类 fstream 的对象同时拥有这两个函数。它们的原型是:
write ( char * buffer, streamsize size );
read ( char * buffer, streamsize size );
这里 buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。
- // reading binary file
- #include <iostream>
- #include <fstream.h>
- const char * filename = "example.txt";
- int main () {
- char * buffer;
- long size;
- ifstream file (filename, ios::in|ios::binary|ios::ate);
- size = file.tellg();
- file.seekg (0, ios::beg);
- buffer = new char [size];
- file.read (buffer, size);
- file.close();
- cout << "the complete file is in a buffer";
- delete[] buffer;
- return 0;
- }
The complete file is in a buffer
缓存和同步(Buffers and Synchronization)
当我们对文件流进行操作的时候,它们与一个streambuf 类型的缓存(buffer)联系在一起。这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。例如,对于一个输出流, 每次成员函数put (写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。
当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。这个过程称为同步(synchronization),它会在以下任一情况下发生:
当文件被关闭时: 在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。
当缓存buffer 满时:缓存Buffers 有一定的空间限制。当缓存满时,它会被自动同步。
控制符明确指明:当遇到流中某些特定的控制符时,同步会发生。这些控制符包括:flush 和endl。
明确调用函数sync(): 调用成员函数sync() (无参数)可以引发立即同步。这个函数返回一个int 值,等于-1 表示流没有联系的缓存或操作失败。
在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
1、插入器(<<)
向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'n';就表示把字符串"Write Stdout"和换行字符('n')输出到标准输出流。
2、析取器(>>)
从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。
一、打开文件
在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
void open(const char* filename,int mode,int access);
参数:
filename: 要打开的文件名
mode: 要打开文件的方式
access: 打开文件的属性
打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:
ios::app: 以追加的方式打开文件
ios::ate: 文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary: 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in: 文件以输入方式打开
ios::out: 文件以输出方式打开
ios::nocreate: 不建立文件,所以文件不存在时打开失败
ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败
ios::trunc: 如果文件存在,把文件长度设为0
可以用“或”把以上属性连接起来,如ios::out|ios::binary
打开文件的属性取值是:
0:普通文件,打开访问
1:只读文件
2:隐含文件
4:系统文件
可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。
例如:以二进制输入方式打开文件c:config.sys
fstream file1;
file1.open("c:config.sys",ios::binary|ios::in,0);
如果open函数只有文件名一个参数,则是以读/写普通文件打开,即:
file1.open("c:config.sys");<=>file1.open("c:config.sys",ios::in|ios::out,0);
另外,fstream还有和open()一样的构造函数,对于上例,在定义的时侯就可以打开文件了:
fstream file1("c:config.sys");
特别提出的是,fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式打开文件,而ofstream默认以输出方式打开文件。
ifstream file2("c:pdos.def");//以输入方式打开文件
ofstream file3("c:x.123");//以输出方式打开文件
所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。
二、关闭文件
打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close();就把file1相连的文件关闭。
三、读写文件
读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式
1、文本文件的读写
文本文件的读写很简单:用插入器(<<)向文件输出;用析取器(>>)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:
file2<<"I Love You";//向文件写入字符串"I Love You"
int i;
file1>>i;//从文件输入一个整数值。
这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
操纵符 功能 输入/输出
dec 格式化为十进制数值数据 输入和输出
endl 输出一个换行符并刷新此流 输出
ends 输出一个空字符 输出
hex 格式化为十六进制数值数据 输入和输出
oct 格式化为八进制数值数据 输入和输出
setpxecision(int p) 设置浮点数的精度位数 输出
比如要把123当作十六进制输出:file1<<hex<<123;要把3.1415926以5位精度输出:file1<<setpxecision(5)<<3.1415926。
2、二进制文件的读写
①put()
put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put('c');就是向流写一个字符'c'。
②get()
get()函数比较灵活,有3种常用的重载形式:
一种就是和put()对应的形式:ifstream &get(char &ch);功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。
另一种重载形式的原型是: int get();这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。
还 有一种形式的原型是:ifstream &get(char *buf,int num,char delim='n');这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符'n'。例如:
file2.get(str1,127,'A');//从文件中读取字符到字符串str1,当遇到字符'A'或读取了127个字符时终止。
③读写数据块
要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:
read(unsigned char *buf,int num);
write(const unsigned char *buf,int num);
read() 从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
例:
unsigned char str1[]="I Love You";
int n[5];
ifstream in("xxx.xxx");
ofstream out("yyy.yyy");
out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中
in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
in.close();out.close();
四、检测EOF
成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
例: if(in.eof())ShowMessage("已经到达文件尾!");
五、文件定位
和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时, 相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
istream &seekg(streamoff offset,seek_dir origin);
ostream &seekp(streamoff offset,seek_dir origin);
streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
ios::beg: 文件开头
ios::cur: 文件当前位置
ios::end: 文件结尾
这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。
例:file1.seekg(1234,ios::cur);//把文件的读指针从当前位置向后移1234个字节
file2.seekp(1234,ios::beg);//把文件的写指针从文件开头向后移1234个字节
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。