13.1使用流
文章目录
1.流的含义
缓冲流和未缓冲流之间的区别是缓冲流不会立即将数据发送到目的地。相反,它会缓冲(即,收集)传入的数据,然后以块的形式分块发送。另一方面,非缓冲流会立即将数据发送到目的地。缓冲通常是为了提高性能,因为某些目的地(如文件)在一次写入更大的块时性能更好。请注意,总是可以通过使用 flush()
方法刷新缓冲流的缓冲区,强制缓冲流将当前缓冲的所有数据发送到目的地.
这些流还存在宽字符版本,以字符w开头∶wcin
、wcout
、wcer
和 wclog
。宽字符可以用于字符数多于英语的语言,例如中文.
有关流的另一个要点是∶流不仅包含普通数据,还包含称为当前位置(current position)的特殊数据。当前位置指的是流将要进行下一次读写操作的位置。
2.流的来源和目的地
在C++中,流可使用3个公共的来源和目的地∶控制台、文件和字符串。
文件流,顾名思义,它从文件系统中读取数据并向文件系统写入数据。文件输入流适用于读取配置数据、读取保存的文件,也适用于批处理基于文件的数据等任务。文件输出流适用于保存状态数据和提供输出等任务。
字符串流是将流隐喻应用于字符串类型的例子。使用字符串流时,可像处理其他任何流一样处理字符数据。就字符串流的大部分功能而言,只不过是为 string类的很多方法能够完成的功能提供了便利的语法。然而,使用流式语法为优化提供了机会,而且比直接使用string类方便得多。
3.流式输出
3.1输出的基本概念
cout
流是写入控制台的内建流,控制台也称为标准输出(standard output)。可将<<
的使用串联起来,从而输出多个数据段。这是由于<<
运算符返回一个流的引用,因此可以立即对同一个流再次应用<<
运算符。
C++流可正确解析C 风格的转义字符,例如包含\n
的字符串,也可使用std::endl
开始一个新行。\n
和 endl
的区别是:\n
仅开始一个新行,而end还会刷新缓冲区。使用endl 时要小心,因为过多的缓冲区刷新会降低性能。
3.2输出流的方法
<ostream>
put()和write()
put()和 write()是原始的输出方法。这两个方法接收的不是定义了输出行为的对象或变量,put()接收单个字符,write()接收一个字符数组。传给这些方法的数据按照原本的形式输出,没有做任何特殊的格式化和处理操作。
flush()
在以下任意一种条件下,流将刷新(或写出)积累的数据∶
- 到endl操作算子时。
- 流离开作用域被析构时。
- 缓冲区满时。
- 显式地要求流刷新缓冲区时。
- 要求从对应的输入流输入数据时(即,要求从 cin 输入时,cout 会刷新)
3.3处理错误输出
当一个流处于正常的可用状态时,称这个流是"好的"。调用流的 good()
方法可以判断这个流当前是否处于正常状态。
通过 good()
方法可方便地获得流的基本验证信息,但不能提供流不可用的原因。还有一个 bad()
方法提供了稍多信息。如果 bad()
方法返回 true
,那么意味着发生了致命错误(相对于非致命错误,例如到达文件结尾 eof()
)。另一个方法 fail()
在最近一次操作失败时返回 true
,但没有说明下一次操作是否也会失败。例如,在对输出流调用 fush()
后,可以通过调用 fail()
来确保刷新成功。
cout.flush();
if(cout.fail()){
cerr << "Unable to flush to standard out" << endl;
}
流具有可转换为 bool类型的转换运算符。转换运算符与调用!fail()
时返回的结果相同。因此,可
将前面的代码段重写为∶
cout.flush();
if(!cout){
cerr << "Unable to flush to standard out" << endl;
}
到文件结束标记时,good()
和 fail()
都会返回 false
。关系如下∶good()==(!fail()&&!eof())
。
还可要求流在发生故障时抛出异常。然后编写一个catch处理程序来捕捉ios_base::failure
异常,然后对这个异常调用what()
方法,来获得错误的描述信息;调用code()
方法获得错误代码。但是,是否能获得有用信息取决于所使用的标准库实现。
cout.exceptions(ios::failbit | ios::badbit | ios::eofbit);
try{
cout << "Hello world." << endl;
}catch(const ios_base::failure& ex){
cerr << "Caught exception: " << ex.what()
<< ", error code = " << ex.code() << endl;
}
可以通过clear()
方法重置流的错误状态.
3.4输出操作算子
C+流还能识别操作算子(manipulator),操作算子是能修改流行为的对象,而不是(或额外提供)流能够操作的数据。大部分定义在<ios>
和<iomanip>
中
boolalpha
和noboolalpha
∶ 要求流将布尔值输出为 true 和 false(boolalpha)或1和0(noboolalpha)。
默认行为是noboolalpha。hex
、oct
和dec
∶分别以十六进制、八进制和十进制输出数字。setprecision
∶设置输出小数时的小数位数。这是一个参数化的操作算子(也就是说,这个操作
算子接收一个参数)。setw
∶设置输出数值数据的字段宽度。这是一个参数化的操作算子。setfil
∶将一个字符设置为流的新的填充字符。填充字符根据 setw 设置的宽度填充输出。这是
一个参数化的操作算子。showpoint
和noshowpoint
∶对于不带小数部分的浮点数,强制流总是显示或不显示小数点。put_money
∶一个参数化的操作算子,向流写入一个格式化的货币值。put_time
∶一个参数化的操作算子,向流写入一个格式化的时间值。quoted
∶一个参数化的操作算子,把给定的字符串封装在引号中,并转义嵌入的引号。
4.流式输入
4.1输入的基本概念
C++中的空白字符包括空格(‘ ’)、换行(\f)、换行(\n))、回车(\r)、水平制表符(\t)和垂直制表符(\v)。
4.2处理输入错误
遇到文件结束标记时, good()
和fail()
都会返回false: good() == (!fail()&&!eof())
4.3输入方法
get()
get()方法允许从流中读入原始输入数据。get()的最简单版本返回流中的下一个字符,其他版本一
次读入多个字符。get()常用于避免>>
运算符的自动标志化
string readName(istream& stream)
{
string name{};
while(stream){
int next {stream.get()};
if(!stream || next == std::char_traits<char>::eof())
break;
name += static_cast<char>(next);
}
return name;
}
- 参数是对istream的非const引用, 因为从流中读入数据的方法会改变实际的流(主要改变当前位置), 因为它们都不是const方法
- get()的返回值保存在int中而不是char中, 因为get()会返回一些特殊的非字符值.
另一个版本的get()接收一个字符串的引用, 并返回一个流的引用. 只有当输出流中没有处于任何错误状态时, 在条件环境中对一个输入流求值时才会得到true.
string readName(istream& stream)lue
{
string name{};
char next{};
while(stream.get(next)){
name += next;
}
return name;
}
unget()
调用unget()会导致流回退一个位置,将读入的前一个字符放回流中。调用 fail()方法可查看unget()是否成功。例如,如果当前位置就是流的起始位置,那么 unget()会失败。
putback()
utback()和 unget()一样,允许在输入流中反向移动一个字符。区别在于 putback()方法将放回流中
的字符接收为参数
peek()
过 peek()方法可预览调用 get()后返回的下一个值
getline()
getline()方法用一行数据填充字符缓冲区,数据量最多至指定大小。指定的大小中包括\0
字符。
调用 getline()时,从输入流中读取一行,读到行尾为止。不过,行尾字符不会出现在字符串中。注意,行尾序列和平台相关。例如,行尾序列可以是\r\n
、\n
或\n\r
。
4.4输入操作算子
4.5对象的输入输出
略
4.6自定义的操作算子
略