C++三大类IO概述

    IO类型定义在三个独立的头文件中:iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,sstream定义的用于读写存储在内存中的string对象。
    iostream类型由istream和ostream两者派生而来,iostream对象共享了它的两个父类的接口。也就是说,可使用iostream类型在同一个流上实现输入和输出操作。

    单独使用istream可以实现输入,单独使用ostream可以实现输出;此外,istream还是ifstream的基类,后者是读取文件输入的库。ostream是ofstream的基类,后者实现文件输出的库,可见,istream(ostream)是一个通用的实现窗口和文件输入(输出)的基类。



一、IO类

头文件iostream
类型istream,wistream从流读取数据
类型ostream,wostream向流写入数据
类型iostream,wiostream读写数据
头文件fstream
类型ifstream,wiftream从文件读取数据
类型oftream,woftream向文件中写入数据
类型fstream,wfstream读写数据
头文件sstream
类型istringstream,wistringstream从string中读取数据
类型ostringstream,wostringstream向string中写入数据
类型stringstream,wstringstream读写数据
(继承,iftream,istingstream都继承与istream,IO对象无拷贝或者赋值)

二、IO条件状态

2.1 查询流的状态

IO操作都有可能发生错误,一些错误是可恢复的,而其他错误发生在系统深处,已经超出了应用程序可以修正的范围。
IO类定义了一些函数和标志,可以帮助我们访问和操纵流的条件状态。
首先表示一个流当前的状态的变量的类型为strm::iostate,其中strm是一种流类型,可以是iostream、fstream等。比如,我们定义一个标准IO流状态:
iostream::iostate strm_state=iostream::goodbit;
IO库存定义了4个iostate类型的contexpr值,表示特定的位模式。这些值用来表示特定类型的IO条件,可以与位运算一起使用来一次性检测或设置多个标志位。
1)strm::badbit用来指定流已崩溃。它表示系统级的错误,如不可恢复的读写错误。通常情况下,一旦badbit被置位,流就无法再使用了。
2)strm::failbit用来指出一个IO操作失败了。
3)strm::eofbit用来指出流达了文件的结束。
在发生可恢复错误后,failbit被置位,如期望读取数值却读出一个字符错误。这种问题通常可以修正,流还可以继续使用。如果到达文件结束位置,eofbit和failbit都会被置位。
4)strm::goodbit用来指出流未处于错误状态。此值保证为零。
goodbit的值为0,表示流未发生错误。如果badbit、failbit和eofbit任一个置位,则检测流状态的条件会失败。
标准库还定义了一组函数来查询这些标志位的状态,假如s是一个流,那么:
s.eof() // 若流s的eofbit置位,则返回true
s.fail() // 若流s的failbit或badbit置位,则返回true
s.bad() // 若流s的badbit被置位,则返回true
s.good() // 若流s处于有效状态,则返回true
在实际我们在循环中判断流的状态是否有效时,都直接使用流对象本身,比如:while(cin>>variable){cout<<variable},在实际中都转换为了while((cin>>variable).good()){cout<<variable}。

2.2 管理条件状态

IO类库提供了3个函数来管理和设置流的状态:


s.clear(); // 将流s中所有条件状态复位,将流的状态设置为有效,调用good会返回true
s.clear(flags); // 根据给定的flags标志位,将流s中对应的条件状态复位
s.setstate(flags); // 根据给定的flags标志位,将流s中对应的条件状态置位。
s.rdstate(); // 返回一个iostate值,对应流当前的状态。


我们可以这样使用上面的这些成员函数。


iostream::iostate old_state = cin.rdstate(); // 记住cin当前的状态
cin.clear(); // 使用cin有效
process_input(cin); // 使用cin
cin.setstate(old_state); // 将cin置为原有状态
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit); // 下failbit和badbit复位,保持eofbit不变。

三、使用文件流

3.1 使用文件流对象


创建一个文件流对象时,我们可以提供文件名,也可不提供文件名,后面用open成员函数来打开文件。

string infile="../input.txt";
string outfile = "../output.txt";
ifstream in(infile); // 定义时打开文件
ofstream out;
out.open(outfile); // 用open打开文件


如果调用open失败,failbit会被置位,所以调用open时进行检测通常是一个好习惯。
如果用一个读文件流ifstream去打开一个不存在的文件,将导致读取失败,而如果用一写文件流ofstream去打开一个文件,如果文件不存在,则会创建这个文件。
一旦一个文件流已经被打开,它就保持与对应文件的关联。实际上,对一个已经打开的文件流调用open会失败,并会导致failbit被置位,随后的试图使用文件流的操作都会失败。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。关闭一个流的关联文件可以用close成员函数来完成。

3.2 文件模式

每个流都有一个关联的文件模式,用来指出如何使用文件,下面列出了文件模式和它们的含义:

in:以读方式打开
out:以写方式打开
app:每次写操作均定位到文件末尾
ate:打开文件后立即定位到文件末尾
trunc:截断文件
binary:以二进制方式进行IO

用文件名初始化一个流时或用open打开文件时都可以指定文件模式,但要注意下面几种限制:

<1>只可以对ofstream或fstream对象设定out模式。
<2>只可以对ifstream或fstream对象设定in模式。
<3>只有out设定时才可以设定trunc模式。
<4>只要trunc没有被设定,就可以设定app模式。在app模式下,即使没有显式指定out模式 ,文件也总是以输出方式被打开。
<5>默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。为了保留以out模式打开的文件的内容,我们必须同时指定app模式,这样只会将数据追加写到文件末尾;或者同时指定in模式,即打开文件同时进行读写操作。
<6>ate和binary模式可以用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。

以out模式打开文件会丢弃已有数据,所以阻止一个ofstream清空给定文件内容的方法是同时指定app模式。

// 在这几条语句中,file1都被截断
ofstream out("file1"); // 隐含以输出模式打开文件并截断文件
ofstream out2("file1", ofstream::out); // 隐含地截断文件
ofstream out3("file1", ofstream::out | ofstream::trunc); // 显式截断文件
// 为了保留文件内容,我们必须显式指定app
ofstream app("file2", ofstream::app); // 隐含为输出模式
ofstream app("file2", ofstream::app | ofstream::out);

seekg(绝对位置); //绝对移动,  //输入流操作 
seekg(相对位置,参照位置);  //相对操作  
tellg();     //返回当前指针位置  

seekp(绝对位置);  //绝对移动, //输出流操作 
seekp(相对位置,参照位置);  //相对操作     
tellp();          //返回当前指针位置  

ios::beg  = 0       //相对于文件头  
ios::cur   = 1       //相对于当前位置  
ios::end  = 2       //相对于文件尾  

四、使用字符流

sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据,就像string是一个IO流一样。

4.1 使用istringstream

很多时候我们需要逐行处理文本,而且需要对行内的单词进行单独分析,这时候使用istringstream是很方便的。

比如,我们程序需要一次读取一行文本,然后将其中的单词分别取出保存在一个vector中。

string line,word;
while (getline(cin, line))
{
vector<string> wordList;
istringstream lineText(line);
while (lineText >> word)
{
wordList.push_back(word);
}
}

4.2 使用ostringstream

当我们逐步构造输出时,希望最后一起打印时,ostringstream是很有用的,它可以帮我们完成类似于itoa,ftoa这种数字转字符串的功能。

int num1 = 42;
double pi = 3.1415926;
string str = "some numbers";

ostringstream formatted;
formatted << str << pi << num1;
cout << formatted.str() << endl;

其中str成员函数是stringstream有几个特有操作之一。

string s;
stringstream strm(s);// 保存s的一个拷贝,此构造函数是explicit的。
strm.str(); // 返回strm所保存的string对象的拷贝。
strm.str(s); // 将s拷贝到strm中,返回void。

五、流的随机访问

不同的流类型一般支持对相关流中数据的随机访问。可以重新定位流,以便环绕跳过,首先读最后一行,再读第一行,以此类推。标准库提供一对函数来定位(seek)给定位置并告诉(tell)相关流中的当前位置。

5.1 seek和tell函数

seekg:重新定位输入流中的标记

tellg:返回输入流中标记的当前位置

seekp:重新定位输出流中的标记

tellp:返回输出流中标记的当前位置

逻辑上,只能在istream或者ifstream或者istringstream上使用g版本,并且只能在ostream类型或其派生类性ofstream或者ostringstream之上使用p版本。iostream对象,fstream或者stringstream对象对相关流既可以读也可以写,可以使用两个版本的任意版本。

5.2 只有一个标记

虽然标准库区分输入和输入而有两个版本,但它只在文件中维持一个标记——没有可区分的读标记和写标记。

只是试图在ifstream对象上调用tellp的时候,编译器将会给出错误提示。反之亦然。

使用既可以读又能写的fstream类型以及stringstream类型的时候,只有一个保存数据的缓冲区和一个表示缓冲器中当前位置的标记,标准库将g版本和p版本都映射到这个标记。

5.3 普通iostream对象一般不允许随机访问。
5.4 重新定位标记

seekg(new_position);
seekp (new_position);
seekg( offset, dir);
seekp( offset, dir);
第一个版本将当前位置切换到给定地点,第二个版本接受一个偏移量以及从何处计算偏移的指示器。

5.5 访问标记

tell函数返回的一个值,使用适当类的pos_type成员来保存。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值