添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
本章介绍了传统 iostream 库并提供了其使用示例,但并未完整介绍 iostream 库。有关更多详细信息,请参见 iostream 库手册页。要访问传统 iostream 手册页,请键入: man -s 3CC4 name

14.1 预定义的 iostream

有四个预定义的 iostream

cin ,连接到 标准输入

除了 cerr 之外,所有预定义的 iostream 都是完全缓冲的。请参见 14.3.1 使用 iostream 进行输出 14.3.2 使用 iostream 进行输入

14.2 iostream 交互的基本结构

通过将 iostream 库包括进来,程序可以使用许多输入流或输出流。每个流都具有某些源或接收器,如下所示:

标准输入、输出和错误由从类 istream ostream 派生的特殊类对象处理。

分别从 istream ostream iostream 派生的 ifstream ofstream fstream 类用于处理文件的输入和输出。

分别从 istream ostream iostream 派生的 istrstream ostrstream strstream 类用于处理字符数组的输入和输出。

打开输入或输出流时,要创建其中一种类型的对象,并将流的 streambuf 成员与设备或文件关联。通常通过流构造函数 执行此关联,因此不用直接使用 streambuf iostream 库为标准输入、标准输出和错误输出预定义了流对象,因此不必为这些流创建自己的对象。

可以使用运算符或 iostream 成员函数将数据插入流(输出)或从流(输入)提取数据,以及控制插入或提取的数据的格式。

如果要插入和提取新的数据类型(其中一个类),通常需要重载插入和提取运算符。

14.3 使用传统 iostream

要使用来自传统 iostream 库的例程,必须针对所需的库部分将头文件包括进来。 下表对头文件进行了具体描述。

表 14–1 iostream 例程头文件

通常程序中不需要所有这些头文件,而仅包括所需声明的头文件。在兼容模式 ( -compat[=4] ) 下,传统 iostream 库是 lib C 的一部分,它由 CC 驱动程序自动链接。在标准模式(缺省模式)下, libiostream 包含 传统 iostream 库。

14.3.1 使用 iostream 进行输出

使用 iostream 进行的输出通常依赖于重载的 左移运算符 ( << )(在 iostream 上下文中,称为插入运算符)。 要将值输出到标准输出,应将值插入预定义的输出流 cout 中。例如,要将给定值 someValue 发送到标准输出,可以使用以下语句:

对于所有内置类型,都会重载插入运算符,且 someValue 表示的值转换为其适当的输出表示形式。例如,如果 someValue float << 运算符会将该值转换为带小数点的适当数字序列。此处是将 float 值插入输出流中,因此 << 称为浮点插入器。 通常,如果给定类型 X << 称为 X 插入器。 ios (3CC4) 手册页中讨论了输出格式以及如何控制输出格式。

iostream 库不支持用户定义的类型 。如果要定义以自己的方式输出的类型,必须定义一个插入器(即,重载 << 运算符)来正确处理它们。

<< 可以多次应用。要将两个值
插入 cout 中,可以使用类似于以下示例中的语句:

cout << a+b;              // + has higher precedence than <<
cout << (a+b);
cout << (a&y);            // << has precedence higher than &
cout << a&y;            // probably an error: (cout << a) & y

14.3.1.1 定义自己的插入运算符

以下示例定义了 string 类:

friend ostream& operator<<(ostream&, const string&); friend istream& operator>>(istream&, string&);

在此示例中,必须将插入运算符和提取运算符定义为友元,因为 string 类的数据部分是 private

运算符 << ostream& (也就是对 ostream 的引用)作为其第一个参数,并返回相同的 ostream ,这样就可以在一个语句中合并多个插入。

14.3.1.2 处理输出错误

通常情况下,重载 operator<< 时不必检查错误,因为有 iostream 库传播错误。

出现错误时,所在的 iostream 会进入 错误状态。 iostream 状态中的各个位根据错误的一般类别进行设置。 iostream 中定义的插入器不会尝试将数据插入处于错误状态的任何流,因此这种尝试不会更改 iostream 的状态。

通常,处理错误的推荐方法是定期检查某些中心位置中输出流的状态。如果有错误,就应该以某些方式来处理。本章假定您定义了函数 error ,该函数可采用字符串并中止程序。 error 不是预定义的函数 。有关 error 函数的示例,请参见 14.3.9 处理输入错误 。可以使用运算符 ! 检查 iostream 的状态 ,如果 iostream 处于错误状态,该运算符会返回非零值。例如:

flush 是一种称为 操纵符 的对象示例,它是一个值,可以插入 iostream 中以起到一定作用,而不是使输出其值。它实际上是一个函数,采用 ostream& istream& 参数,在对其执行某些操作后返回其参数(请参见 14.7 操纵符 )。

14.3.1.4 二进制输出

要获得原始二进制形式的值输出,请使用以下示例所示的成员函数 write 。该示例显示了原始二进制形式的 x 输出。

以上示例将 &x 转换为 char* ,这违反了类型规程。一般情况下,这样做无关大碍,但如果 x 的类型是具有指针、虚拟成员函数的类或是需要 nontrivial 构造函数操作的类,就无法正确读回以上示例写入的值。

14.3.2 使用 iostream 进行输入

使用 iostream 进行的输入类似于输出。需要使用提取 运算符 >> ,可以像插入操作那样将提取操作串接在一起。例如:

该语句从标准输入获得两个值。与其他重载运算符一样,所用的提取器取决于 a b 的类型(如果 a b 的类型不同,则使用两个不同的提取器)。 ios (3CC4) 手册页中详细讨论了输入格式以及如何控制输入格式。通常,前导空白字符(空格、换行符、标签、换页等)被忽略。

14.3.3 定义自己的提取运算符

要输入新的类型时,如同重载输出的插入运算符,请重载输入的提取运算符。

string 定义了其提取运算符,如以下代码示例所示:


示例 14–1 string 提取运算符

istream& operator>> (istream& istr, string& input)
    const int maxline = 256;
    char holder[maxline];
    istr.get(holder, maxline, ”\n’);
    input = holder;
    return istr;
get 函数从输入流 istr 读取字符,并将其存储在 holder 中,直到读取了 maxline-1 字符、遇到新行或 EOF(无论先发生哪一项)。然后,holder 中的数据以空终止。最后,holder 中的字符复制到目标字符串。

按照约定,提取器转换其第一个参数中的字符(此示例中是 istream& istr),将其存储在第二个参数(始终是引用),然后返回第一个参数。因为提取器会将输入值存储在第二个参数中,所以第二个参数必须是引用。

14.3.4 使用 char* 提取器

此处提及这个预定义的提取器是因为它可能产生问题。使用方法如下:

cin.unsetf(ios::skipws); // turn off whitespace skipping
cin.setf(ios::skipws); // turn it on again

可以使用 iostream 操纵符 ws iostream 中删除前导空白,不论是否启用了跳过功能。以下示例说明了如何从 iostream istr 中删除前导空白:

cin >> bad; if (!cin) error("aborted due to input error"); cout << "If you see this, not an error." << "\n"; return 0;

ios 具有可用于错误处理的成员函数。详细信息请参见手册页。

14.3.10 结合使用 iostream stdio

可以将 stdio 用于 C++ 程序,但在程序内的同一标准流中混合使用 iostream stdio 时,可能会发生问题。例如,如果同时向 stdout cout 写入,会发生独立缓冲,并产生不可预料的结果。如果从 stdin cin 两者进行输入,问题会更加严重,因为独立缓冲可能会破坏输入。

要消除标准输入、标准输出和标准错误中的这种问题,就请在执行输入或输出前使用以下指令:它将所有预定义的 iostream 与相应的预定义 stdio 文件连接起来。

因为在预定义流作为连接的一部分成为无缓冲流时,性能会显著下降,所以该连接不是缺省连接。可以在应用于不同文件的同一程序中同时使用 stdio iostream 。也就是说,可以使用 stdio 例程向 stdout 写入,并向连接到 iostream 的其他文件写入。可以打开 stdio 文件进行输入,也可以从 cin 读取,只要不同时尝试从 stdin 读取即可。

14.4 创建 iostream

要读取或写入不是预定义的 iostream 的流,需要创建自己的 iostream 。通常,这意味着创建 iostream 库中所定义类型的对象。本节讨论了可用的各种类型:

14.4.1 使用类 fstream 处理文件

处理文件类似于处理标准输入和标准输出;类 ifstream ofstrea m fstream 分别从类 istream ostream iostream 派生而来。作为派生的类,它们继承了插入和提取运算符(以及其他成员函数),还有与文件一起使用的成员和构造函数。

可将文件 fstream.h 包括进来以使用任何 fstream 。如果只要执行输入,请使用 ifstream ;如果只要执行输出,请使用 ofstream ;如果要对流执行输入和输出,请使用 fstream 。将文件名称用作构造函数参数。

例如,将文件 thisFile 复制到文件 thatFile ,如以下示例所示:

ifstream fromFile("thisFile");
if     (!fromFile)
    error("unable to open ’thisFile’ for input");
ofstream toFile ("thatFile");
if     (!toFile)
    error("unable to open ’thatFile’ for output");
char c;
while (toFile && fromFile.get(c)) toFile.put(c);

使用 ios::in 的缺省模式创建名为 fromFile ifstream 对象,并将其连接到 thisFile 。然后打开 thisFile

enum open_mode {binary=0, in=1, out=2, ate=4, app=8, trunc=0x10,
     nocreate=0x20, noreplace=0x40};

注 –

UNIX 中不需要 binary 标志,提供该标志是为了与需要它的系统兼容。可移植代码在打开二进制文件时要使用 binary 标志。

您可以打开文件同时用于输入和输出。例如,以下代码打开了文件 someName 用于输入和输出,同时将其连接到 fstream 变量 inoutFile

如果通过向 fstream 构造函数之一提供文件名或使用 open 函数来打开文件,则在销毁 fstream (通过 delete 销毁或其超出作用域)时,会自动关闭该文件。将文件 attach fstream 时,不会自动关闭该文件。

14.4.1.5 在文件内重新定位

您可以在文件中改变读取和写入的位置。有多个工具可以达到这个目的。

streampos 是可以记录 iostream 中的位置的类型。

streampos original = aFile.tellp();     //save current position
aFile.seekp(0, ios::end); //reposition to end of file
aFile << x;               //write a value to file
aFile.seekp(original);    //return to original position
seekg ( seekp ) 可以采用一个或两个参数。如果有两个参数,第一个参数是相对于 seek_dir 值(也就是第二个参数)指示的位置的位置。例如:

iostream 不允许将一个流赋值给另一个流。

复制流对象的问题是有两个可以独立更改的状态信息版本,例如输出文件中指向当前写入位置的指针。因此,某些问题会发生。

14.6 格式控制

ios (3CC4) 手册页中详细讨论了格式控制。

14.7 操纵符

操纵符是可以在 iostream 中插入或提取以起到特殊作用的值。

参数化操纵符是具有一个或多个参数的操纵符。

因为操纵符是普通的标识符,因此会用完可能的名称,而 iostream 不会为每个可能的函数定义操纵符。本章的其他部分讨论了各种操纵符和成员函数。

有 13 个预定义的操纵符,如 表 14–2 中所述。使用该表时,要进行以下假设:

i 的类型为 long

头文件 iomanip.h 中定义了多个类。每个类都保存一个操纵符函数的地址和一个参数的值。 manip (3CC4) 手册页中介绍了 iomanip 类。 上面的示例使用了 smanip_int 类,它是与 ios 一起使用。因为该类与 ios 一起使用,所以也可以与 istream ostream 一起使用。上面的示例还使用了另一个类型为 int 的参数。

applicator 创建并返回类对象。在上面的代码示例中,类对象是 smanip_int ,其中包含了操纵符和 applicator 的 int 参数。 iomanip.h 头文件定义了用于该类的移位运算符。如果 applicator 函数 setfill 在输入或输出操作序列中,会调用该 applicator 函数,且其返回一个类。 移位运算符作用于该类,以使用其参数值(存储在类中)调用操纵符函数。

在以下示例中,操纵符 print_hex

将输出流设置成十六进制模式。

#include <iomanip.h> static ostream& xfield(ostream& os, long v) { long save = os.setf(ios::hex, ios::basefield); os << v; os.setf(save, ios::basefield); return os; omanip_long print_hex(long v) { return omanip_long(xfield, v);

14.8 用于数组的 strstream : iostream

请参见 strstream (3CC4) 手册页。

14.9 用于 stdio 文件的 stdiobuf : iostream

请参见 stdiobuf (3CC4) 手册页。

14.10 streambuf iostream 是由两部分(输入或输出)构成的系统的格式化部分。系统的其他部分由处理无格式字符流的输入或输出的 streambuf 组成。

通常,可以通过 iostream 使用 streambuf ,因此,不必担心 streambuf 的细节。您可以选择直接使用 streambuf ,例如,如果需要提高效率或解决 iostream 内置的处理或格式化错误。

14.10.1 streambuf 工作方式

streambuf 由字符流或字符序列和一个或两个指向相应序列的指针组成。每个指针都指向两个字符间。(实际上,指针无法指向字符间,但可以按这种方式考虑指针。)有两种 streambuf 指针:

put 指针,它指向下一个字符的存储位置前面

streambuf 可以有其中一个指针,也可以两个全有。

14.10.1.1 指针位置

可以使用多种方法来操作指针的位置和序列的内容。操作两个指针时它们是否都会移动取决于使用 streambuf 种类。通常,如果使用 队列式 streambuf,get 和 put 指针独立移动;如果使用文件式 streambuf,get 和 put 指针总是一起移动。例如, strstream 是队列式流, fstream 是文件式流。

14.10.2 使用 streambuf

从来不创建实际的 streambuf 对象,而是只创建从 streambuf 类派生的类的对象。例如 filebuf strstreambuf filebuf (3CC4) 手册页和 ssbuf (3) 手册页中分别对它们进行了介绍。高级用户可能想从 streambuf 派生自己的类,以便提供特定设备的接口或提供基本缓冲以外的功能。 sbufpub (3CC4) 和 sbufprot (3CC4) 手册页中讨论了如何执行此操作。

除了创建自己的特殊种类的 streambuf 外,您可能还想通过访问与 iostream 关联的 streambuf 来访问公用成员函数(如上面引用的手册页中所述)。此外,每个 iostream 都有采用 streambuf 指针的已定义插入器和提取器。插入或提取 streambuf 时,会复制整个流。

下面是另一种文件复制(前面讨论过)方法,这里为了清晰起见省略了错误检查:

该词有两个含义,一个特定于 iostream 软件包,另一个较常适用于输入和输出。

iostream 库特定相关时,缓冲区是由类 streambuf 定义的类型的对象。

通常,缓冲区是一个内存块,用于将字符高效传输到输出的输入。对于已缓冲的 I/O,缓冲区已满或被强制刷新之前,字符的实际传输会延迟。

无缓冲的缓冲区是指在其中没有上文定义的通用意义的缓冲区的 streambuf 。本章避免使用缓冲区一词来指 streambuf 。但是,手册页和其他 C++ 文档使用缓冲区一词来表示 streambuf