`
zhaohaolin
  • 浏览: 1010681 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

C++方式的文件操作

 
阅读更多

本文主要讨论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 重新定位输入流中的读指针 tellg 返回输入流中读指针的当前位置 seekp 重新定位输出流中的写指针 tellp 返回输出流中写指针的当前位置 
注意:在每个流中(即使是可同时输入输出的流,如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);

分享到:
评论

相关推荐

    C++ 文件操作大全

    在 ANSI C 中,对文件的操作分为两种方式,即流式文件操作和 I/O 文件操作。流式文件操作是通过 FILE 结构体来实现的,FILE 结构体包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行。 流式文件...

    c++ 各种文件操作

    ### C++ 文件操作详解 #### 一、概述 在 C++ 中,文件操作是一项非常重要的功能,它允许程序与磁盘上的数据进行交互。本文档将详细介绍 C++ 中的文件操作,包括文件的打开、关闭、读取、写入等基本操作以及一些...

    C++文件操作工具类

    "C++文件操作工具类"是一个专门为C++开发者设计的实用工具,它简化了对文件进行读写、创建、删除等操作的过程。 首先,我们要理解C++中的文件操作基本概念。C++通过标准库中的`fstream`头文件提供了一套接口,允许...

    C++文件读写_c++文件读写_文件操作c++_c++文件读写_

    总结,C++文件读写涉及的主要知识点包括:文件流的概念,文件的打开与关闭,数据的读写,文件定位,错误处理,以及打开模式等。通过这些知识点,开发者可以实现对文件的高效、灵活操作。在实际项目中,根据需求选择...

    c++文件操作大全

    本资源"**C++文件操作大全**"提供了一个全面的教程,覆盖了从创建、读写、修改属性到复制和删除文件的各种操作,旨在帮助开发者深入理解C++中的文件系统操作。 首先,我们来看一下C++如何进行基本的文件读写操作。...

    c++对文件的操作

    "C++对文件的操作" C++对文件的操作是指在C++编程中对文件进行的各种操作,包括读取、打开、创建、查找、修改、更新、删除等。这些操作都是通过流类(stream)来实现的。流类是C++中的一种输入/输出机制,它允许...

    C C++ C++ Builder的各种文件读写操作总结

    在 ANSI C 中,对文件的操作分为两种方式,即流式文件操作和 I/O 文件操作。在流式文件操作中,使用 FILE 结构体来表示文件对象,该结构体包含了文件操作的基本属性。 FILE 结构体的定义如下: typedef struct { ...

    C++文件操作详解

    C++文件操作 C++文件的读写fstream // 文件流输入文件流输出文件流 创建一个文本文件并写入信息同向屏幕上输出信息一样将信息输出至文件

    C++操作系统文件管理系统设计与实现

    4. **内存映射文件**:为了提高大文件的读写效率,可以使用内存映射文件技术,将文件内容映射到进程的虚拟内存空间,使得文件操作就像操作内存一样快速。 5. **文件权限和安全**:文件系统需要维护文件的权限信息,...

    C++ 操作dbf文件(深入剖析dbf文件)

    总结来说,C++操作DBF文件涉及到理解DBF文件的内部结构,使用C++的I/O流进行文件操作,以及处理不同数据类型的转换。通过自定义的`Dbf`类,我们可以方便地读取、写入和查询DBF文件,而无需依赖Visual Foxpro驱动,...

    C++操作Excel文件

    这个过程相对复杂,不适用于频繁的文件操作。 3. **OpenCV库**:OpenCV提供了`cv::FileStorage`类,可以读写XML或YAML文件。对于简单的数据读写,可以考虑用它来处理Excel数据。然而,这通常不是最佳选择,因为它...

    C++ 文件操作ppt

    在C++编程中,文件操作是一项基础且重要的技能。文件被定义为存储在外部介质上的一组相关信息的集合,它可以包含各种类型的数据,如文本、图像、音频等。文件的名称是唯一的,由文件路径、主名和扩展名三部分组成。...

    c++对asc码文件的存取操作

    在文件操作完成后,需要关闭文件流对象,释放系统资源。 ```c infile.close(); ``` 七、总结 本篇文章详细介绍了C++语言中对ASC码文件的存取操作,包括文件的读取、写入和修改操作。使用fstream库可以实现文件的...

    Visual C++ 文件读写操作

    通过这些类,我们可以方便地进行文件操作。 1. 打开文件: 使用`ifstream`或`ofstream`类的构造函数可以打开一个文件。例如,打开一个名为“example.txt”的文件用于读取,可以这样写: ```cpp std::ifstream ...

    C++实现文件读写操作

    C++实现文件的读出写入操作,并实现对文件中空格的删除操作。

    C++操作Dbf文件

    功能:操作DBF文件的C++代码,可以创建DBF文件、读取DBF文件、写入DBF文件、拷贝DBF文件的结构 环境:需要C++11和BOOST库,需要设置环境变量BOOST_HOME指向BOOST的根目录 例子:见main.cpp

    文件系统C++实现

    本文将深入探讨一个用C++实现的文件系统模拟器,该系统能够执行常见的文件操作,如创建、删除和修改文件,并且采用了树形结构和位示图来管理磁盘空间。 首先,我们来看“文件系统”的概念。文件系统是一种逻辑组织...

    C++ 的各种文件读写操作总结

    C++引入了`fstream`库,提供了更面向对象的文件操作方式。主要类包括`ifstream`(读取文件),`ofstream`(写入文件)和`fstream`(读写文件)。 1. **ifstream**:构造时指定文件名,可以使用`open()`方法打开文件...

    c++操作xml文件

    C++作为一款强大的编程语言,提供了多种方式来处理XML文件,本篇文章将重点讲解如何使用C++进行XML文件的操作。 首先,我们来看标题"**c++操作xml文件**",这表明我们要讨论的是如何在C++程序中读取、写入或修改XML...

    c++批量重命名文件

    在IT行业中,C++是一种强大的、面向对象的编程语言,被广泛用于系统软件、应用软件、游戏开发以及大规模数据处理等。...这个工具不仅适用于个人使用,还可以作为学习C++文件操作和用户交互的实例,提升编程能力。

Global site tag (gtag.js) - Google Analytics