一、IO类
1、IO库类型和头文件表:
头文件 | 类型 |
iostream |
istream,wistream 从流读取数据 ostream,wostream 向流写入数据 iostream,wiostream 读写流 |
fstream |
ifstream,wifstream 从文件读取数据 ofstream,wofstream 向文件写入数据 fstream,wfstream 读写文件 |
sstream |
istringstream,wistringstream 从string读取数据 ostringstream,wostringstream 向string写入数据 stringstream,wstringstream 读写string |
为了支持使用宽字符的语言,标准库定义一组类型和对象来操纵wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始。
类fstream和stringstream都是继承自类iostream的。输入类都继承自istream,输出类都继承自ostream。因此,可以在istream对象上执行的操作,也可以在ifstream或istringstream对象上执行。继承自ostream的输出类也是类似的情况。
2、IO对象无拷贝或赋值
我们不能拷贝或对IO对象赋值。进行IO操作的函数通常以引用方式传递和返回值。读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。
3、条件状态
IO类定义的一些函数和标志:
条件状态 | 说明 |
strm::iostate | strm是一种IO类型。iostate是一种机器相关的类型,提供了表达条件状态的完整功能。 |
strm::badbit | 用来指出流以崩溃。 |
strm::failbit | 用来指出一个IO操作失败了。 |
strm::eofbit | 用来指出流到达了文件结束。 |
strm::goodbit | 用来指出流未处于错误状态。此值保证为0。 |
s.eof() | 若流的eofbit置位,则返回true。 |
s.fail() | 若流的failbit或badbit置位,则返回true。 |
s.bad() | 若流的badbit置位,则返回true。 |
s.good() | 若流s处于有效状态,则返回true。 |
s.clear() | 将流中所有条件状态复位,将流的状态设置为有效。返回void。 |
s.clear(flags) | 根据给定的flags标志位,将流s中对应条件状态位复位。flags的类型为strm::iostate。返回void。 |
s.setstate(flags) | 根据给定的flags标志位,将流s中对应条件状态位置位。flags的类型为strm::iostate。返回void。 |
s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::iostate。 |
badbit表示系统级错误,如不可恢复的读写错误。通常情况下,一旦badbit被置位,流就无法再使用了。
在发生可恢复错误后,failbit被置位,如期望读取数值却读出一个字符等错误。这种问题通常是可以修正的,流还可以继续使用。
如果到达文件结束位置,eofbit和failbit都会被置位。
goodbit的值为0,表示流未发生错误。
如果badbit、failbit、eofbit任一个被置位,则检测流状态的条件会失败。
4、管理输出缓冲
1)缓冲区
每个输出流都管理一个缓冲区,用来保存程序读写的数据。有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统写操作。由于设备的写操作可能很耗时,允许操作系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。
导致缓冲刷新(即,数据真正写到输出设备或文件)的原因有很多:
a、程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。
b、缓冲区满时,需要刷新缓冲而后新的数据才能继续写入缓冲区。
c、我们可以使用操纵符endl来显示刷新缓冲区。
d、在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来情况缓冲区。默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。
e、一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到流的缓冲区会被立即刷新。例如,默认情况下,cin和cerr都关联到cout。因此。读cin或写cerr都会导致cout的缓冲区被刷新。
2)刷新输出缓冲
我们已经使用过操纵符endl,它完成换行并刷新缓冲区的工作。IO库还有两个类似的操纵符:flush和ends。flush刷新缓冲区,但不输出额外的字符;ends想缓冲区插入一个空字符,然后刷新缓冲区。
#include <iostream>
#include <string>
#include <fstream>
#include <sstream> int main()
{
std::cout << "hi!" << std::endl;
std::cout << "hi!" << std::ends;
std::cout << "hi!" << std::flush;
return ;
}
3)unitbuf操纵符
如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符。它告诉流在接下来的每次写操作之后都进行一次flush操作。而nounitbuf操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制:
#include <iostream> int main()
{
std::cout << std::unitbuf;
// 任何输出都立即输出,无缓冲
std::cout << std::nounitbuf;
return ;
}
注意:如果程序异常中止,输出缓冲区是不会被刷新的。当一个程序崩溃后,它所输出的数据很可能停留在输出缓冲区中等待打印。
4)关联输入和输出流
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。
tie有两个重载的版本:一个版本不带参数,返回指向输出流的指针。如果本对象当前关联到一个输出流,则返回的就是指向这个流的指针,如果对象未关联到流,则返回空指针。tie的第二个版本接受一个指向ostream的指针,将自己关联到此ostream。即,x.tie(&o)将流x关联到输出流o。
我们既可以把一个输入流关联到另一个输出流,也可以将一个输出流关联到另一个输出流:
#include <iostream> int main()
{
std::cin.tie(&std::cout); // 标准库将cin和cout关联在一起
// old_tie指向当前关联到cin的流
std::ostream *old_tie = std::cin.tie(nullptr); // cin不再与其他流关联
std::cin.tie(&std::cerr); // cin与cerr关联
std::cin.tie(old_tie); // cin恢复正常关联
return ;
}
每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。
二、文件输入输出
头文件fstream定义了三个类型来支持文件IO:ifstream从一个给定文件读取数据,ofstream向一个给定文件写入数据,fstream可以读写文件。这些类型提供的操作和对象cin和cout的操作一样。
fstream特有的操作:
操作 | 说明 |
fstream fstrm; | 创建一个未绑定的文件流。fstream是头文件fstream中定义的一个类型 |
fstream fstrm(s); | 创建一个fstream,并打开名为s的文件。s可以是string类型,或者是一个指向C风格字符串的指针。这些构造函数都是explicit的。默认的文件模式mode依赖于fstream的类型 |
fstream fstrm(s, mode); | 与前一个构造函数类似,但按指定mode打开文件 |
fstrm.open(s) | 打开名为s的文件,并将文件与fstrm绑定,s可以是string类型或者是一个指向C风格字符串的指针。默认的文件模式mode依赖于fstream的类型。返回void |
fstrm.close() | 关闭与fstrm绑定的文件。返回void |
fstrm.is_open() | 返回一个bool值,指出与fstrm关联的文件是否成功打开且尚未关闭 |
1、使用文件流对象
创建文件流对象时,我们可以提供文件名(可选的)。如果提供了一个文件名,则open会自动调用。
#include <iostream>
#include <fstream>
#include <string> int main()
{
std::ofstream out("hello.txt");
out << "QAQ" << std::endl;
out.close();
std::ifstream in;
in.open("hello.txt");
if (in.is_open())
{
std::string str;
in >> str;
in.close();
std::cout << str << std::endl;
}
return ;
}
1)成员函数open和close
如果我们定义了一个空文件流对象,可以随后调用open来将它与文件关联起来。如果调用open失败,failbit会被置位。因为调用open可能失败,进行open是否成功的检测通常是很有必要的。
#include <iostream>
#include <fstream>
#include <string> int main()
{
std::ifstream in;
in.open("hello.txt");
if (in) // 检测open是否成功
{
std::string str;
in >> str;
in.close();
std::cout << str << std::endl;
}
return ;
}
一旦一个文件流已经打开,它将保持与对应文件的关联。实际上,对一个已经打开的文件流调用open会失败,并会导致failbit被置位。随后的试图使用文件流的操作都会失败。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。一旦文件成功关闭,我们可以打开新的文件。
#include <iostream>
#include <fstream>
#include <string> int main()
{
std::ifstream in;
in.open("hello.txt");
in.close();
in.open("hello2.txt");
if (in) // 检测open是否成功
{
std::string str;
in >> str;
in.close();
std::cout << str << std::endl;
}
return ;
}
2)、自动构造和析构
当一个fstream 对象离开其作用域时会被销毁,close()会自动被调用。
2、文件模式
每个流都有一个关联的文件模式,用来指出如何使用文件。
模式 | 说明 |
in | 以读方式打开 |
out | 以写方式打开 |
app | 每次写操作均定位到文件末尾 |
ate | 打开文件后立即定位到文件末尾 |
trunc | 截断文件 |
binary | 以二进制方式进行IO |
无论用哪种方式打开文件,我们都可以指定文件模式,调用open打开文件时可以,用一个文件名初始化流来隐式打开文件时也可以。指定文件模式有以下限制:
a、只可以对ofstream和fstream对象设定out模式
b、只可以对ifstream和fstream对象设定in模式
c、只有当out也被设定时才可以设定trunc模式
d、只要trunc没被设定,就可以设定app模式。在app模式下,即使没有显示指定out模式,文件也总是以输出方式打开
e、默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。为了保留以out模式打开的文件的内容,我们必须同时指定app模式,这样只会把将数据追写到文件末尾;或者同时指定in模式,即打开文件同时进行读写操作
f、ate和binary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合
每个文件流类类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。与ifstream关联的文件默认以in模式打开;与ofstream关联的文件默认以out模式打开;与fstream关联的文件默认以in和out模式打开。
默认情况下,当我们打开一个ofstream时,文件的内容会被丢弃。阻止一个ofstream情况给定文件内容的方法是同时指定app模式。
对于一个给定流,每当打开文件时,都可以改变其文件模式。
#include <iostream>
#include <fstream>
#include <string> int main()
{
std::ofstream o; // 以输出模式打开文件并截断文件
std::ofstream out("hello.txt", std::ofstream::out|std::ofstream::app); // 保留了文件的内容
out << "QAQ" << std::flush;
out.close();
std::ifstream in;
in.open("hello.txt", std::ifstream::in);
if (in) // 检测open是否成功
{
std::string str;
in >> str;
in.close();
std::cout << str << std::endl;
}
return ;
}
三、string流
sstream头文件定义了三个类型来支持内存IO:istringstream从string读取数据,ostringstream向string写入数据,而stringstream既可以从string读取数据也可向string写数据。
sstream的三个类型的特有的操作:
操作 | 描述 |
sstream strm; | strm是一个未绑定的stringstream对象。sstream是头文件sstream中定义的一个类型 |
sstream strm(s); | strm是一个sstream对象,保存string s的一个拷贝。此构造函数是explicit的 |
strm.str() | 返回strm所保存的string的拷贝 |
strm.str(s) | 将string s拷贝到strm中 |
1)使用istringstream
当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream。
#include <iostream>
#include <sstream>
#include <string> int main()
{
std::istringstream in;
in.str("hello world 233");
std::string word;
while (in >> word)
{
std::cout << word << std::endl;
}
return ;
}
2)使用ostringstream
当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的。
#include <iostream>
#include <sstream>
#include <string> int main()
{
std::ostringstream out;
out << "hello ";
out << "world ";
out << "";
std::cout << out.str() << std::endl;
return ;
}