C++文件读取

1. IO类

1.1 不同的IO类型

1. istream和ostream
IO库定义了读写内置类型值的操作。
istream 类型,提供输入操作。例如 cin,从标准输入读取数据。
ostream 类型,提供输出操作。例如 cout,向标准输出写入数据。
为了支持不同类型的IO处理操作,在 istream 和 ostream 之外,标准库还定义了其他一些IO类型。
2. fstream和sstream
fstream类定义了读写命名文件的类型,其中:
ifstream类型,从文件读取数据;ofstream类型,向文件写入数据。
sstream类定义了读写内存对象的类型,其中:
istringstream类型,从string读取数据;ostringstream类型,向string写入数据。

1.2 不同IO类型之间的关系

设备类型和字符大小都不会影响我们要执行的 IO 操作。例如,我们可以用 cin 读取数据,而不用管是从一个控制台窗口,一个磁盘文件还是一个 string 读取。标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制实现的。类型 ifstream 和 istringstream 都继承自 istream。因此可以像使用 istream 对象一样来使用 ifstream 和 istringstream 对象。

1.3 IO对象无拷贝或赋值

我们不能拷贝或对 IO 对象赋值,如下所示:

ofstream out1, out2;
out1 = out2;                  // 错误,不能对流对象赋值
ofstream print(ofstram);      // 错误,不能初始化ofstream参数
out2 = print(out2);           // 错误,不能拷贝流对象

由于不能拷贝 IO 对象,因此也不能将形参或返回类型设置为流类型,进行 IO 操作的函数通常以引用方式传递和返回流。读写一个 IO 对象会改变其状态,因此传递和返回的引用不能是 const 的。

1.4 条件状态

IO 操作一个与生俱来的问题是可能发生错误。一些错误是可恢复的,而其他错误则发生在系统深处,已经超过了应用程序可以修正的范围。下面列出了 IO 类所定义的一些函数和标志,可以帮助我们访问和操纵流的状态条件。

函数含义
strm::iostatestrm 是一种 IO 类型,iostate 是一种机器相关的类型,提供了表达条件状态的完整功能
strm::badbit指流已崩溃
strm::failbit指出一个 IO 操作失败了
strm::eofbit指出流到达了文件结束
strm::goodbit指流未处于错误状态。此值保证为 0
s.eof( )若流 s 的 eofbit 置位,则返回 true
s.fail( )若流 s 的 failbit 或 badbit 置位,则返回 true
s.bad( )若流 s 的 badbit 置位,则返回 true
s.good( )若流 s 处于有效状态,则返回 true
s.clear( )将流 s 中所有条件状态复位,将流的状态设置为有效,返回 void
s.clear(flags)根据给定的 flags 标志位,将流 s 中对应条件状态位复位。flags 的类型为 strm::iostate。返回 void
s.setstate(flags)根据给定的 flags 标志位,将流 s 中对应条件状态位置位。flags 的类型为 strm::iostate。返回 void
s.rdstate( )返回流 s 的当前条件状态,返回值类型为 strm::iostate

1.查询流的状态
将流作为条件使用,只能告诉我们流是否有效,而无法告诉我们具体发生了什么。有时我们也需要知道流为什么失败。IO 库定义了一个与机器无关的iostate类型,它提供了表达流状态的完整功能,这个类型应作为一个位集合使用。IO 库定义了四个 iostate 类型的 constexpr 值,表示特定的位模式。这些值用来表示特定类型的 IO 条件,可以与位运算符一起使用来一次性检测或设置多个标志位。
badbit表示系统级错误,如不可恢复的读写错误。通常情况下,一旦 badbit 被置位,流就无法再使用了。在发生可恢复错误后,failbit 被置位,如期望读取数值却读出一个字符等错误。这种问题通常可以修正,流还可以继续使用。如果到达文件结束位置,eofbit 和 failbit 都会被置位。goodbit 的值为0,表示流未发生错误。如果 badbit、failbit 和 eofbit 任一个被置位,则检测流状态的条件会失败。
2.管理条件状态
流对象的rdstate成员返回一个iostate值,对应流的当前状态。setstate 操作将给定条件位置位,表示发生了对应错误。clear 成员是一个重载的成员,它有一个不接受参数的版本,另一个版本接受一个 iostate 类型的参数。

1.5 管理输出缓冲

每个输出流都管理一个缓冲区,用来保存程序读写的数据。例如:

os << "please enter a value";

文本串可能立即打出来,但也有可能被操作系统保存在缓冲区内,随后再打印。有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。导致缓冲刷新的原因很多,如下:
1.程序正常结束,作为 main 函数的 return 操作的一部分,缓冲刷新被执行。
2.使用操纵符如 endl 来显式刷新缓冲区。
1.刷新输出缓冲区
endl: 完成换行并刷新缓冲区的工作;
flush: 刷新缓冲区但不输出任何额外的字符;
ends: 向缓冲区插入一个空字符然后刷新缓冲区;
2.unitbuf操纵符
如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符,它告诉流在接下来的每次写操作后都进行一次 flush 操作,而nounitbuf操作符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制。

cout << unitbuf;    // 任何输出都立即刷新
.....
cout << nounitbuf;  // 回到正常的缓冲方式

2. 文件输入输出

fstream 中定义的类型还增加一些新的成员来管理与流关联的文件。

函数含义
fstream fstrm创建一个未绑定的文件流
fstream fstrm(s)创建一个 fstream,并打开名为 s 的文件
fstream fstrm(s, mode)与前一个函数类似,但按指定类型 mode 打开文件
fstrm.open(s)打开名为 s 的文件,并将文件与 fstrm 绑定
fstream.close( )关闭与 fstrm 绑定的文件
fstrm.is_open( )返回一个bool 值,指出与 fstrm 关联的文件是否成功打开且尚未关闭

2.1 使用文件流对象

当我们想要读写一个文件时,可以定义一个文件流对象,并将对象与文件关联起来。每个文件流类都定义了一个名为 open 的成员函数,它完成一些系统相关的操作,定位给定的文件并视情况打开为读或写模式。创建文件流对象时,我们可以提供文件名,如果提供了一个文件名,则 open 会自动调用。

ifstream in(ifile);   // 构造一个ifstream并打开文件

1.用fstream代替iostream&
在要求使用基类型对象的地方,我们可以用继承类型的对象来替代。这意味着接受一个 iostream 类型的引用(或指针)参数的函数,可以用一个对应的 fstream 或 sstream 类型来调用。
2.成员函数open和close
如果我们定义了一个空文件流对象,可以随时调用 open 来将它与文件关联起来。如果调用 open 失败,failbit 会被置位。因为调用 open 可能会失败,进行 open 是否成功的检测通常是一个好习惯。一旦一个文件流打开,它就保持与对应文件的关联。实际上对一个已经打开的文件流调用 open 会失败,并会导致 failbit 被置位。随后的试图使用文件流的操作都会失败。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。
3.自动构造和析构
当一个 fstream 对象被销毁时,close 会自动被调用。

2.2 文件模式

每个流都有一个关联的文件模式。用来指出如何使用文件。

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

指定文件模式有如下限制:
1.只可以对 ofstream 或 fstream 对象设定 out 模式;
2.只可以对 istream 或 fstream 对象设定 in 模式;
3.只有当 out 也被设定时才可设定 trunc 模式;
4.只要 trunc 没被设定,就可以设定 app 模式。
在 app 模式下,即使没有显式指定 out 模式,文件也总是以输出方式被打开。默认情况下,即使没有指定 trunc,以 out 模式打开的文件也会被截断。为了保留以 out 模式打开的文件的内容,我们必须指定 app 模式,这样只会将数据追加写到文件末尾;或者同时指定 in 模式,即打开文件的同时进行读写操作。每个文件流类型都指定了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。与 istream 关联的文件默认以 in 模式打开;与 ofstream 关联的文件默认以 out 模式打开;与 fstream 关联的文件默认以 in 和 out 模式打开。默认情况下,当我们打开一个 ofstream 时,文件的内容会被丢弃,阻止一个 ofstream 清空给定文件内容的方法是同时指定 app 模式。

ofstream out("file", ofstream:: app );

1.每次调用open时都会确定文件模式
在每次打开文件时,都要设置文件模式,可能是显式地设置,也可能是隐式的设置。

3. string流

sstream头文件定义了三个类型来支持内存 IO,这些类型可以向 string 写入数据,从 string 读取数据。istringstream 从 string 读取数据,ostringstream 向 string 写入数据,stringstream既可以从 string 读数据也可向 string 写数据。

函数含义
sstream strmstrm 是一个未绑定的 stringstream 对象,sstream 是头文件 sstream 中定义的一个类型
sstream strm(s)strm 是一个 sstream 对象,保存 string s 的一个拷贝。此构造函数是 explicit 的
strm.str()返回 strm 所保存的 string 拷贝
strm.str(s)将 string s 拷贝到 strm 中

3.1 使用istringstream

当某些工作是对整行文本进行处理,而其他的一些工作是处理行内的单个单词时,通常可以使用 istringstream。

struct Person{
   string name;
   vector<string> phones;
};
string line, word;
vector<Person> poeple;
while(getline(cin, line)){
     Person  info;
     istringstream record(line);
     record >> info.name;
     while(record >> word)
        info.phones.push_back(word);
     people.push_back(info);
}

3.2 使用ostringstream

当我们逐步构造输出,希望最后一起打印时,ostringstream 是很有用的。例如,对上一节的例子,我们可能想逐个验证电话号码并改变其格式。如果所有号码都是有效的我们希望输出一个新文件,包含改变格式后的号码。对于那些无效的号码,我们不会将它们输出到新文件中,而是打印一条包含人名和无效号码的错误信息。

for(const auto & enter:people)
{
   ostringstream formatted, badNums;
   for(const auto & nums:enter.phones){
      if(!valid(nums)){
         badNums << " " <<nums;
       }
      else
         formatted << " " << format(nums);
}
   if(badNums.str().empty())
      os << enter.name << " " << formatted.str() << endl;
   else
      cerr << "input error:" << entry.name << "invalid number(s)" << badNums.str() << endl;
}
  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Litle_Pudding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值