注:本文为 “C++ *stream” 相关文章合辑。
英文引文,机翻未校。
中文引文,略作重排,未整理去重。
如有内容异常,请看原文。
Understanding the Utility of Iostreams in C++
理解 C++ 中 iostream 的用途
By Manoj Debnath
February 27, 2020
The iostream classes are the first library classes we encounter when we begin with C++. The primary services that we deal with these classes is solving general I/O problems. After all, this is what the name apparently means. But the beauty is that it represents standard I/O, files, and even blocks of memory and they look the same with one interface. The article explores some of the facets of this incredible class with simple example.
当我们开始学习 C++ 时,iostream 类是我们遇到的第一个库类。我们使用这些类的主要目的是解决一般的输入输出问题。毕竟,这正是它的名字所表达的含义。然而,它的美妙之处在于,它不仅代表标准输入输出和文件,甚至还可以表示内存块,而且所有这些都通过一个统一的接口来实现。本文通过一个简单的例子探讨了这个令人惊叹的类的一些方面。
What is iostream in C++?
C++ 中的 iostream 是什么?
The C++ language deals with I/O a bit differently than other languages. It is handled by a family of types prescribed in the standard library. The I/O to and from device files, consoles, and in-memory I/O, such as from string, are all handled by these types. The primary functions defined are read/write operations of the built-in types. Therefore, the I/O stream library provides formatted as well as unformatted buffered I/O of text as well as numeric values.
C++ 语言在处理输入输出时与其他语言有所不同。它是通过标准库中定义的一系列类型来处理的。这些类型负责处理来自设备文件、控制台以及内存中的输入输出(例如来自 string 的数据)。其主要功能是执行内置类型的读写操作。因此,输入输出流库提供了对文本和数值的格式化以及非格式化的缓冲输入输出。
What is a Stream in C++?
C++ 中的流是什么?
When a sequence of bytes flows from one interface to another, it can be called a stream. This sequence of bytes represents information that may be further processed or used as is, depending upon the context. When the flow of bytes sequence is main memory inbound, such as from devices like a keyboard, disk, or network connection, it is called an input operation. And, when the flow of bytes is main memory outbound to a device such as printer, disk, network connection, or display screen, it is called an output operation. The sequence of bytes can have different meanings depending upon the context. It can represent characters, graphical images, raw data, audio/video information, or any other type depending upon the context of use.
当一系列字节从一个接口流向另一个接口时,可以将其称为一个 stream(流)。这一系列字节表示的信息可以根据上下文进一步处理或直接使用。当字节序列流入主内存时,例如来自键盘、磁盘或网络连接等设备,这被称为 input operation(输入操作)。而当字节序列从主内流出到打印机、磁盘、网络连接或显示器等设备时,这被称为 output operation(输出操作)。字节序列的含义可以根据上下文有所不同,它可以表示字符、图形图像、原始数据、音频/视频信息,或者根据使用上下文的任何其他类型。
The I/O mechanism must be able to transfer the sequence of bytes in a consistent and efficient manner. But, the problem is that the devices associated with the I/O are often slow in operation. For example, the mechanical rotation of a disk, input coming from keyboard strokes, or sequence of bytes from remote interface require considerably more time than the time required by the processor to process the data. Therefore, to mitigate the time gap so that it does not lag the overall performance of the I/O operation, the C++ stream library classes are fine tuned for optimal performance.
输入输出机制必须能够以一致且高效的方式传输字节序列。然而,问题在于与输入输出相关的设备通常操作速度较慢。例如,磁盘的机械旋转、来自键盘按键的输入,或者来自远程接口的字节序列,都需要比处理器处理数据所需的时间多得多。因此,为了减少这种时间差,以免拖慢整个输入输出操作的性能,C++ 的流库类经过了优化,以实现最佳性能。
Another problem is that data usually comes in different formats. Some come as an unformatted I/O format, which require low-level I/O processing, and some comes in formatted I/O format, which require high-level I/O capabilities. The unformatted I/O specified by sequence of bytes requires processing of individual bytes during its transfer from device to memory or memory to device. Such data often comes in high volume and require fast processing capabilities. In formatted I/O, the sequence of bytes forms a unit of meaningful combination of bytes such as characters, floating-point numbers, integers, strings, or any other user-defined types. Handling I/O with such data units is convenient but inefficient, especially when they come in high volume. The standard C++ stream library supports both the format and does both high- and low-level I/O processing, respectively.
另一个问题是,数据通常以不同的格式出现。有些是以非格式化的输入输出格式出现,需要进行低级输入输出处理;而有些是以格式化的输入输出格式出现,需要高级输入输出功能。以字节序列指定的非格式化输入输出在从设备传输到内存或从内存传输到设备时,需要逐字节处理。这种数据通常数据量很大,需要快速处理能力。在格式化输入输出中,字节序列形成了一个有意义的字节组合单元,例如字符、浮点数、整数、字符串或任何其他用户定义的类型。使用这种数据单元进行输入输出操作虽然方便,但效率较低,尤其是在数据量很大时。标准 C++ 流库同时支持这两种格式,并分别进行高级和低级输入输出处理。
Character Sets
字符集
Previously, the C++ library used to support only input and output of chars which carried only one byte of information, such as those we find with the ASCII (American Standard Code for information Interchange) (8 - bit/1 - byte) character set. But later, it introduced Unicode characters, which represent extensive international character sets, the I/O capabilities of C++ encompassed them within its framework. For example, the type wchar_t (4 - byte/32 - bit) is a later inclusion and can store Unicode characters. Also, C++11 introduced other types, such as char16_t and char32_t, to represent character types that require explicit sizes. The stream classes were redesigned as specialized templates for dealing with char and wchar_t, respectively.
以前,C++ 库仅支持单字节信息的字符输入输出,例如我们在 ASCII(美国信息交换标准代码)(8 位/1 字节)字符集中所见到的字符。但后来,它引入了 Unicode 字符,这些字符代表广泛的国际字符集,C++ 的输入输出功能将其纳入了其框架中。例如,类型 wchar_t(4 字节/32 位)是后来加入的,可以存储 Unicode 字符。此外,C++11 引入了其他类型,如 char16_t 和 char32_t,以表示需要明确大小的字符类型。流类被重新设计为专门的模板,分别用于处理 char 和 wchar_t。
Stream Classes and Objects
流类和对象
A C++ program includes a header to avail the basic service required for all stream I/O operations. There are four objects defined by iostream: cin, cout, cerr, and clog that correspond to the standard input stream, standard output stream, standard error stream (unbuffered), and standard error stream (buffered), respectively. Each of them provides both formatted and unformatted I/O services.
C++ 程序通过包含 头文件来获取所有流输入输出操作所需的基本服务。iostream 定义了四个对象:cin、cout、cerr 和 clog,分别对应标准输入流、标准输出流、标准错误流(未缓冲)和标准错误流(缓冲)。它们每一个都提供格式化和非格式化的输入输出服务。
There is another header, called , which provides parameterized stream manipulators for performing formatted I/O such as setw, setbase, setprecision, setfill, setiosflags, and more.
另外还有一个名为 的头文件,它提供了用于执行格式化输入输出的参数化流操纵器,例如 setw、setbase、setprecision、setfill、setiosflags 等。
The header is particularly used in association with file processing and provides services for file I/O.
头文件特别用于文件处理,提供文件输入输出服务。
The library stream classes form hierarchy as follows. Figure 1 gives you a quick idea on some of their traits.
库流类形成了如下层次结构。图 1 为您快速展示了它们的一些特征。
Figure 1: The library stream classes’ hierarchy
图 1:库流类的层次结构
-
The ios_base class is the base class for all stream classes of the standard I/O library. It defines common properties of all stream objects, independent of their character type, such as functions for state and format flags.
ios_base 类是标准输入输出库中所有流类的基类。它定义了所有流对象的通用属性,这些属性与其字符类型无关,例如用于状态和格式标志的函数。 -
The basic_ios is a template class derived from ios_base. It defines a stream component independent of input or output, but depends on character types and their traits. They provide the buffer definition, such as the object from the template class basic_streambuf. They are used for the read/write scheme.
basic_ios 是从 ios_base 派生的模板类。它定义了一个与输入或输出无关的流组件,但依赖于字符类型及其特征。它们提供了缓冲区定义,例如来自模板类 basic_streambuf 的对象。它们用于读写方案。 -
The basic_istream and basic_ostream are template classes derived from basic_ios. They define the objects used for read and write operations, respectively. These classes too are parameterized with character types and its traits.
basic_istream 和 basic_ostream 是从 basic_ios 派生的模板类。它们分别定义了用于读取和写入操作的对象。这些类也以字符类型及其特征为参数。 -
The basic_streambuf is the core of the I/O stream library. They provide the interface for read and write operations of the stream.
basic_streambuf 是输入输出流库的核心。它们提供了流的读取和写入操作的接口。
We accept input via descendants of the istream classes and output via descendants of the ostream classes. The iostream classes combine both and provide an object to do input as well as output. Apart from these, the iostream library provides classes for ifstream file input, ofstream for file output, and fstream for both input as well as output operation on files. Similarly, there are classes, such as istringstream, ostringstream, and stringstream, for I/O operation with string classes. They almost have a same interface whether we work with a file, standard I/O, memory, or string object. The stream classes discussed above are the templatized versions to leverage generic type I/O operation.
我们通过 istream 类的派生类接收输入,通过 ostream 类的派生类进行输出。iostream 类结合了两者,提供了一个既可以输入也可以输出的对象。除了这些,iostream 库还提供了用于文件输入的 ifstream 类、文件输出的 ofstream 类,以及用于文件输入输出操作的 fstream 类。同样,还有用于与字符串类进行输入输出操作的类,例如 istringstream、ostringstream 和 stringstream。无论我们是处理文件、标准输入输出、内存还是字符串对象,它们几乎都具有相同的接口。上述讨论的流类是模板化版本,用于实现通用类型的输入输出操作。
A Quick Example
一个快速示例
Here is a quick demonstration of object input/output by overloading ostream and istream operators. The properties of the rudimentary Date class object get inserted and extracted conveniently using stream operators.
下面是一个通过重载 ostream 和 istream 运算符来实现对象输入输出的快速示例。通过使用流运算符,可以方便地插入和提取基础的 Date 类对象的属性。
#ifndef DATE_H
#define DATE_H
#include <string>
class Date
{
int day , month , year; // 私有成员变量,用于存储日期的天、月、年
public:
Date(); // 构造函数声明,用于初始化日期对象
friend std::ostream& operator<<(std::ostream&, const Date&); // 重载 << 运算符,用于输出 Date 对象
friend std::istream& operator>>(std::istream&, Date&); // 重载 >> 运算符,用于输入 Date 对象
};
#endif // DATE_H
#include "date.h"
#include <ctime>
#include <sstream>
#include <iomanip>
using namespace std;
Date::Date()
{
time_t today = time(0); // 获取当前时间
tm *lt = localtime(&today); // 将时间转换为本地时间结构
year = lt->tm_year + 1900; // 设置年份(tm_year 是从 1900 年开始的年数)
month = lt->tm_mon + 1; // 设置月份(tm_mon 是从 0 开始的月份,需要加 1)
day = lt->tm_mday; // 设置日期
}
ostream& operator<<(ostream& os, const Date& d) {
os << d.day << '/'<<d.month<<'/'<<d.year; // 以 dd/mm/yyyy 的格式输出日期
return os; // 返回输出流对象
}
istream& operator>>(istream& is, Date& d) {
is >> d.day >> d.month >> d.year; // 从输入流中读取日期的天、月、年
return is; // 返回输入流对象
}
#include <iostream>
#include "date.h"
using namespace std;
int main()
{
Date d; // 创建一个 Date 对象,默认为当前日期
Date d2; // 创建另一个 Date 对象
cout << d << endl; // 输出当前日期
cout << "Enter Date (in dd/mm/yyyy):";
cin >> d2; // 输入一个日期
cout << d2 << endl; // 输出输入的日期
return 0;
}
Conclusion
结论
The istream object provides formatted and unformatted input capabilities. There are member functions, like get and getline, that overcome the limitation of extraction operators (>>), which skip white - space characters such as blanks, tabs, and newline. Similarly, ostream provides formatted and unformatted output capabilities. There are member functions, such as put, along with the stream insertion operator (<<) to output standard data types. We, however, can overload these operators to give a new meaning as we have done in the example above. Note that here we have merely scratched the surface of iostream library. There are many other aspects. Stay tuned for more.
istream 对象提供了格式化和非格式化的输入功能。它有一些成员函数,如 get 和 getline,这些函数克服了提取运算符(>>)的限制,因为提取运算符会跳过空白字符,如空格、制表符和换行符。同样,ostream 提供了格式化和非格式化的输出功能。它有一些成员函数,如 put,以及流插入运算符(<<),用于输出标准数据类型。然而,正如我们在上面的示例中所做的那样,我们可以重载这些运算符以赋予它们新的含义。请注意,这里我们只是对 iostream 库进行了初步的探索。
The C++ I/O System
C++ 输入输出系统
I/O Class Hierarchy (simplified)
I/O 类层次结构(简化版)
ios_base
|
ios
/ \
istream ostream
| \ / |
| iostream |
| | |
ifstream fstream ofstream
The C++ I/O system defined in the IOStreams library is quite complex, extraordinarily useful and flexible, and quite beautiful in design. The current system resides in the namespace std, but is the result of at least a decade of evolution fuelled by theory, experiment, and the ISO standardization process.
C++ I/O 系统在 IOStreams 库中定义,它非常复杂,极其有用且灵活,设计上也非常优美。当前的系统位于 std
命名空间中,但它是经过至少十年的理论研究、实验和 ISO 标准化过程演变而来的。
A comprehensive treatment of C++ IOStreams is well beyond the scope of these notes. In fact there is a very good 640 page treatise on the subject that is recommended for further study (see [Standard C++ IOStreams and Locales Advanced Programmers Guide and Reference, by Angelika Langer and Klaus Kreft, Addison Wesley, 2000]). What is intended here is a detailed introduction to the most useful instantiations in this library, sufficient for most programming course work. For professional levels of expertise, you are strongly encouraged to obtain the Langer & Kreft reference and keep it alongside your Stroustrup.
对 C++ IOStreams 的全面介绍超出了这些笔记的范围。实际上,有一本非常好的关于该主题的 640 页专著,推荐用于进一步学习(参见 [《Standard C++ IOStreams and Locales Advanced Programmers Guide and Reference》,作者:Angelika Langer 和 Klaus Kreft,Addison Wesley,2000])。这里的目标是详细介绍该库中最常用的实例化,足以满足大多数编程课程的需求。对于专业级别的知识,强烈建议您获取 Langer 和 Kreft 的参考书籍,并将其放在您的 Stroustrup 书籍旁边。
A portion of the iostreams class hierarchy is shown in the slide. This is the portion that we will discuss in some detail. A more complete hierarchy is as follows:
slide 中展示了 iostreams 类层次结构的一部分。我们将详细讨论这一部分。更完整的层次结构如下:
ios_base
|
basic_ios<charT, traits>
/ \
basic_istream<charT, traits> basic_ostream<charT, traits>
| \ / |
| basic_iostream<charT, traits> |
| | |
basic_ifstream<charT, traits> | basic_ofstream<charT, traits>
|
basic_fstream<charT, traits>
Notice that all of the classes except the base class ios_base are templates. The two template parameters represent the character type being used along with properties of that type. There are two predefined character types, the familiar 1 - byte ascii characters char and the 4 - byte wide characters wchar_t. The type char supports ASCII, EBCDIC, and ISO 8859 - 2 character sets. The type wchar_t supports Unicode and ISO 10646. The user can invent other character classes and associated traits and instantiate streams using that type.
请注意,除了基类 ios_base
外,所有类都是模板。两个模板参数表示正在使用的字符类型及其属性。有两种预定义的字符类型:熟悉的 1 字节 ASCII 字符 char
和 4 字节宽字符 wchar_t
。char
类型支持 ASCII、EBCDIC 和 ISO 8859 - 2 字符集。wchar_t
类型支持 Unicode 和 ISO 10646。用户可以创建其他字符类及其相关特性,并使用该类型实例化流。
Even the diagram above does not illustrate the full generality of the IOStream library. There is support for internationalization in the form of Locales, and there is a hierarchy of buffer types that are used by iostreams. These are interesting and important features that are beyond the scope of these notes.
即使上面的图表也没有展示 IOStream 库的全部通用性。它支持以 Locales 形式的国际化,并且有一个被 iostreams 使用的缓冲区类型的层次结构。这些是超出这些笔记范围的有趣且重要的特性。
There is also support for exception handling built into the class hierarchy shown above, but we will not say much about that aspect either. Because we will assume the standard ASCII character set, there is no need to go into detail on the traits classes in these notes. Finally, there is a parallel set of classes for I/O into strings instead of streams.
在上述类层次结构中还内置了异常处理支持,但我们也几乎不会讨论这一方面。由于我们将假设使用标准 ASCII 字符集,因此这些笔记中无需详细讨论特性类。最后,还有一组平行的类用于对字符串而不是流进行输入输出。
For most purposes, it suffices to use the ASCII character set and stream I/O, and there are type definitions in the standard library associated with those assumptions:
对于大多数用途,使用 ASCII 字符集和流式 I/O 就足够了,标准库中有一些与这些假设相关的类型定义:
typedef basic_ios<char, char_traits<char>> ios;
typedef basic_istream<char, char_traits<char>> istream;
typedef basic_ostream<char, char_traits<char>> ostream;
typedef basic_iostream<char, char_traits<char>> iostream;
typedef basic_ifstream<char, char_traits<char>> ifstream;
typedef basic_ofstream<char, char_traits<char>> ofstream;
typedef basic_fstream<char, char_traits<char>> fstream;
In the remainder of this chapter we will discuss details of the classes ios_base (equivalently class ios), the derived classes istream, ostream, iostream, ifstream, ofstream, and the collection of IO manipulators defined with these classes.
在本章的其余部分,我们将讨论 ios_base
类(等同于 ios
类)、派生类 istream
、ostream
、iostream
、ifstream
、ofstream
以及与这些类一起定义的 I/O 操纵符的细节。
Class ios_base
ios_base
类
namespace std {
class ios_base {
public:
// ios_base status methods
bool good() const; // true iff no error flag is set
bool eof() const; // true iff stream is at end of file
bool fail() const; // true iff badbit or failbit are set
bool bad() const; // true if badbit is set
operator void*() const; // null pointer if fail(), non-null otherwise
void clear(iostate newstate = goodbit); // sets state to newstate
void setstate(iostate addstate); // adds addstate to existing state
enum iostate {
goodbit = 0x0000, // everything's ok
eofbit = 0x0001, // stream is at end of file
failbit = 0x0002, // last I/O operation failed
badbit = 0x0004 // serious error, stream unusable
};
protected:
unsigned long state; // stores status bits
};
}
I/O streams can be understood by exploring in detail the class ios_base, which contains all of the public member variables and most public methods in the hierarchy. This slide shows the basic organization of the class. We explore the specifics in more detail in the following slides.
通过详细研究 ios_base
类,可以理解 I/O 流。该类包含层次结构中的所有公共成员变量和大多数公共方法。此文展示了该类的基本组织结构。我们将在接下来的文中更详细地探讨具体细节。
Items to take note of at this point are:
需要注意的几点是:
- All of the standard library classes are in the
std
namespace. - 标准库中的所有类都在
std
命名空间中。 - The implementation details hinted at in this slide and elaborated later are not specified by the standard; we are illustrating one possible instantiation.
- 本文中暗示的实现细节以及后续详细说明的内容并未由标准指定;我们展示的是一种可能的实例化方式。
- Variables and methods related to exception handling are omitted.
- 省略了与异常处理相关的变量和方法。
ios_base Status Methods
ios_base
状态方法
class ios_base {
public:
bool good() const; // true if no error flag is set
bool eof() const; // true if eofbit is set
bool fail() const; // true if badbit or failbit are set
bool bad() const; // true if badbit is set
operator void*() const; // null pointer if fail(), non-null otherwise
void clear(iostate newstate = goodbit); // sets state to newstate
void setstate(iostate addstate); // adds addstate to existing state
enum iostate {
goodbit = 0x0000, // everything's ok
eofbit = 0x0001, // stream is at end of file
failbit = 0x0002, // last I/O operation failed
badbit = 0x0004 // serious error, stream unusable
};
protected:
unsigned long state; // stores status bits
};
Some useful code techniques can be illuminated with this information. For example:
利用这些信息可以展示一些有用的编码技巧。例如:
std::ifstream in;
in.open(filename);
while (!in) {
std::cout << "Cannot open file " << filename << "-try again:";
std::cin >> filename;
in.clear();
in.open(filename);
}
The implementation of setstate uses bitwise operations. These are native operations of C that allow direct access to integral values at the bit level. Most current CPUs have hardware support for bitwise operations, making them extremely efficient, typically requiring one clock cycle to accomplish the entire operation.
setstate
的实现使用了位运算。这些是 C 的原生操作,允许直接在位级别访问整数值。大多数现代 CPU 都有硬件支持位运算,这使得它们极其高效,通常只需要一个时钟周期即可完成整个操作。
Here is a table showing the bitwise operations and their semantics:
以下表格展示了位运算及其语义:
Operation | Symbol | Type | Infix Version | Accumulator Version |
---|---|---|---|---|
and | & | binary | z = x & y | z &= y |
or | | | binary | z = x | y | z |= y |
xor | ^ | binary | z = x ^ y | z ^= y |
not | ~ | unary | z = ~y | (na) |
left shift | << | binary | z = x << n | (na) |
right shift | >> | binary | z = x >> n | (na) |
ios_base Formatting Methods
ios_base
格式化方法
class ios_base {
public:
fmtflags flags() const; // returns current flags
fmtflags flags(fmtflags newflags); // sets flags to newflags
fmtflags setf(fmtflags setbits); // sets specified flags
fmtflags setf(fmtflags setbits, fmtflags mask); // sets flags in mask
fmtflags unsetf(fmtflags unsetbits); // clears specified flags
enum fmtflags {
boolalpha = 0x0001, // read/write bool values in alphabetic
left = 0x0002, // left-justify output
right = 0x0004, // right-justify output
internal = 0x0008, // prefix left..fill..number right
skipws = 0x0010, // skip white space before extraction
dec = 0x0020, // decimal
hex = 0x0040, // hexadecimal
oct = 0x0080, // octal
showbase = 0x0100, // show base indicator on output
showpoint = 0x0200, // show decimal point for fixed point output
showpos = 0x0400, // force show of sign for positive numbers
fixed = 0x0800, // force decimal notation for float
scientific = 0x1000, // force scientific notation for float
unitbuf = 0x2000, // flush buffer after each insertion
uppercase = 0x4000 // use upper case indicators for hex and e
};
protected:
unsigned long flags; // stores flag bits
};
Meaning of Format Flags
格式标志的含义
Format Flag | Meaning |
---|---|
boolalpha | Reads and writes bool values in alphabetic |
left | Left-justify output |
right | Right-justify output |
internal | Prefix left…fill…number right |
skipws | Skip white space before extraction |
dec | Decimal |
hex | Hexadecimal |
oct | Octal |
showbase | Show base indicator on output |
showpoint | Show decimal point for fixed point output |
showpos | Force show of sign for positive numbers |
fixed | Force decimal notation for float |
scientific | Force scientific notation for float |
unitbuf | Flush buffer after each insertion |
uppercase | Use upper case indicators for hex and e |
ios_base Data Methods
ios_base
数据方法
class ios_base {
public:
char fill(char fillch); // sets fill character
int precision() const; // returns precision value
int precision(int val); // sets precision value
int width() const; // returns width value
int width(int val); // sets width value
protected:
int width_value; // initialized to 0
int precision_value; // initialized to 0
char fill_character; // initialized to ' '
};
Meaning of Width and Precision
宽度和精度的含义
- Width:
n <= 0
: No effectn > 0
: On output, sets the minimum number of characters output (filled with fill char). On input, sets the length of buffer for string extractions. Note: Reset to 0 after each insertion and extraction.
- Precision:
n < 0
: Defaultn >= 0
: Ifios::fixed
is set, determines the number of places displayed after the decimal point. Ifios::fixed
is not set, determines the number of significant digits displayed.
ios_base Binding Methods
ios_base
绑定方法
class ios_base {
public:
streambuf* rdbuf(); // returns ptr to stream's streambuf object
ostream* tie(); // returns ptr to the tied ostream
ostream* tie(ostream*); // ties current stream to specified ostream
static bool sync_with_stdio(bool sync = true); // sync with C standard I/O
protected:
streambuf* streambuffer; // pointer to a streambuf object
ostream* tied_ostream; // pointer to an ostream object
};
ios_base File Modes
ios_base
文件模式
class ios_base {
public:
enum open_mode {
in = 0x0001, // open file for input
out = 0x0002, // open file for output
ate = 0x0004, // seek to end when file is opened
app = 0x0008, // open file in append mode
trunc = 0x0010, // truncate the file if it exists
binary = 0x0020 // open file in binary mode
};
enum seek_dir {
beg = 0x0100, // seek relative to beginning of file
cur = 0x0200, // seek relative to current position
end = 0x0400 // seek relative to end of file
};
protected:
unsigned long mode; // stores open and seek mode bits
};
Meaning of File Modes
文件模式的含义
Open Mode | Effect | Effect with binary |
---|---|---|
in | Opens text files for reading, initial position at beginning of file | Initial position at beginning of file |
out | Truncates file to empty, or creates file for write only | No effect on empty file |
`out | trunc` | Truncates file to empty, or creates file for write only |
app | Appends; opens or creates text file for writing at end of file | No additional effect |
`in | out` | Opens file for update (read or write), position at beginning of file |
`in | out | trunc` |
Possible ios_base Method Implementations
可能的 ios_base
方法实现
ios_base::fmtflags ios_base::flags() const {
return flags;
}
ios_base::fmtflags ios_base::flags(ios_base::fmtflags newflags) {
ios_base::fmtflags oldflags = flags;
flags = newflags;
return oldflags;
}
ios_base::fmtflags ios_base::setf(ios_base::fmtflags setbits) {
ios_base::fmtflags oldflags = flags;
flags |= setbits;
return oldflags;
}
ios_base::fmtflags ios_base::setf(ios_base::fmtflags setbits, ios_base::fmtflags mask) {
ios_base::fmtflags oldflags = flags;
flags = (flags & ~mask) | (setbits & mask);
return oldflags;
}
ios_base::fmtflags ios_base::unsetf(ios_base::fmtflags unsetbits) {
ios_base::fmtflags oldflags = flags;
flags &= ~unsetbits;
return oldflags;
}
char ios_base::fill(char newfill) {
char oldfill = fill_character;
fill_character = newfill;
return oldfill;
}
int ios_base::precision() const {
return precision_value;
}
int ios_base::precision(int val) {
int oldprecision = precision_value;
precision_value = val;
return oldprecision;
}
int ios_base::width() const {
return width_value;
}
int ios_base::width(int val) {
int oldwidth = width_value;
width_value = val;
return oldwidth;
}
Class istream
istream
类
namespace std {
class istream : public ios_base {
public:
friend istream& operator>>(istream&, char&);
friend istream& operator>>(istream&, int);
friend istream& operator>>(istream&, long);
friend istream& operator>>(istream&, unsigned char);
friend istream& operator>>(istream&, unsigned int);
friend istream& operator>>(istream&, unsigned long);
friend istream& operator>>(istream&, float);
friend istream& operator>>(istream&, double);
friend istream& operator>>(istream&, long double);
friend istream& operator>>(istream&, char*);
void get(char&);
char get();
char peek();
// predefined object
cin;
};
}
Class ostream
ostream
类
namespace std {
class ostream : public ios_base {
public:
friend ostream& operator<<(ostream&, char);
friend ostream& operator<<(ostream&, int);
friend ostream& operator<<(ostream&, long);
friend ostream& operator<<(ostream&, unsigned char);
friend ostream& operator<<(ostream&, unsigned int);
friend ostream& operator<<(ostream&, unsigned long);
friend ostream& operator<<(ostream&, float);
friend ostream& operator<<(ostream&, double);
friend ostream& operator<<(ostream&, long double);
friend ostream& operator<<(ostream&, const char*);
void put(char ch);
// predefined objects
cout, cerr, clog;
};
}
Predefined Objects cin, cout, cerr, clog
预定义对象 cin
, cout
, cerr
, clog
- Predefined
istream
object:cin
预定义的istream
对象:cin
- Predefined
ostream
objects:cout
,cerr
,clog
预定义的ostream
对象:cout
,cerr
,clog
- Buffered
ostream
s:cout
,clog
缓冲的ostream
:cout
,clog
- Tied stream pair:
cin
,cout
绑定的流对:cin
,cout
cin.tie(&cout);
// in file iostream
cin.tie(&cout);
// 在文件 iostream 中
Class ifstream
ifstream
类
class ifstream : public istream {
public:
ifstream* open(const char* filename, ios_base::open_mode mode = ios_base::in);
ifstream* close();
};
Class ofstream
ofstream
类
class ofstream : public ostream {
public:
ofstream* open(const char* filename, ios_base::open_mode mode = ios_base::out | ios_base::trunc);
ofstream* close();
};
I/O Manipulators - 1
I/O 操纵符 - 1
boolalpha // calls s.setf(ios::boolalpha)
noboolalpha // calls s.unsetf(ios::boolalpha)
noshowbase // calls s.unsetf(ios::showbase)
showbase // calls s.setf(ios::showbase)
showpoint // calls s.setf(ios::showpoint)
noshowpoint // calls s.unsetf(ios::showpoint)
showpos // calls s.setf(ios::showpos)
noshowpos // calls s.unsetf(ios::showpos)
uppercase // calls s.setf(ios::uppercase)
nouppercase // calls s.unsetf(ios::uppercase)
skipws // calls s.setf(ios::skipws)
noskipws // calls s.unsetf(ios::skipws)
unitbuf // calls s.setf(ios::unitbuf)
nounitbuf // calls s.unsetf(ios::unitbuf)
left // calls s.setf(ios::left, ios::adjustfield)
right // calls s.setf(ios::right, ios::adjustfield)
internal // calls s.setf(ios::internal, ios::adjustfield)
dec // calls s.setf(ios::dec, ios::basefield)
hex // calls s.setf(ios::hex, ios::basefield)
oct // calls s.setf(ios::oct, ios::basefield)
fixed // calls s.setf(ios::fixed, ios::floatfield)
scientific // calls s.setf(ios::scientific, ios::floatfield)
endl // flushes streambuf and inserts '\n'
ends // flushes streambuf and inserts '\0'
flush // flushes streambuf
I/O Manipulators - 2
I/O 操纵符 - 2
Prototype for 0 - parameter manipulators:
stream_type& manip_name(stream_type& s);
// User can define and use manipulators, e.g.:
ostream& beepbeep(ostream& os) {
os << "lala";
return os;
}
I/O Manipulators - 3
I/O 操纵符 - 3
setbase(int b); // sets base (radix) for numericals
setiosflags(ios::format_flags mask); // calls ios::setf(mask)
resetiosflags(ios::format_flags mask); // calls ios::unsetf(mask)
setfill(char ch); // calls ios::fill(ch)
setprecision(int n); // calls ios::precision(n)
setw(int n); // calls ios::width(n)
// User may define a more extensive collection
For complete details on all these features of C++ IOStreams, see the Langer and Kreft reference.
有关 C++ IOStreams 的所有这些特性的详细信息,请参阅 Langer 和 Kreft 的参考书籍。
For most purposes, it suffices to use the ASCII character set and stream I/O, and there are type definitions in the standard library associated with those assumptions:
对于大多数用途,使用 ASCII 字符集和流式 I/O 就足够了,标准库中有一些与这些假设相关的类型定义:
typedef basic_ios<char, char_traits<char>> ios;
typedef basic_istream<char, char_traits<char>> istream;
typedef basic_ostream<char, char_traits<char>> ostream;
typedef basic_iostream<char, char_traits<char>> iostream;
typedef basic_ifstream<char, char_traits<char>> ifstream;
typedef basic_ofstream<char, char_traits<char>> ofstream;
typedef basic_fstream<char, char_traits<char>> fstream;
Note that making these substitutions yields the hierarchy illustrated in the slide.
请注意,进行这些替换后,将得到文中展示的层次结构。
In the remainder of this chapter we will discuss details of the classes ios_base (equivalently class ios), the derived classes istream, ostream, iostream, ifstream, ofstream, and the collection of IO manipulators defined with these classes.
在本章的其余部分,我们将讨论 ios_base
类(等同于 ios
类)、派生类 istream
、ostream
、iostream
、ifstream
、ofstream
,以及与这些类一起定义的 I/O 操纵符的细节。
Class ios_base
ios_base
类
namespace std {
class ios_base {
public:
// ios_base status methods
bool good() const; // true if no error flag is set
bool eof() const; // true if stream is at end of file
bool fail() const; // true if badbit or failbit are set
bool bad() const; // true if badbit is set
operator void*() const; // null pointer if fail(), non-null otherwise
void clear(iostate newstate = goodbit); // sets state to newstate
void setstate(iostate addstate); // adds addstate to existing state
enum iostate {
goodbit = 0x0000, // everything's ok
eofbit = 0x0001, // stream is at end of file
failbit = 0x0002, // last I/O operation failed
badbit = 0x0004 // serious error, stream unusable
};
protected:
unsigned long state; // stores status bits
unsigned long flags; // stores mode bits
unsigned long mode; // stores flag bits
int width_value; // initialized to 0
int precision_value; // initialized to 0
char fill_character; // initialized to ' '
streambuf* streambuffer; // pointer to a streambuf object
// plus other data, such as specifying tied streams
};
}
I/O streams can be understood by exploring in detail the class ios_base, which contains all of the public member variables and most public methods in the hierarchy. This slide shows the basic organization of the class. We explore the specifics in more detail in the following slides.
通过详细研究 ios_base
类,可以理解 I/O 流。该类包含层次结构中的所有公共成员变量和大多数公共方法。此文展示了该类的基本组织结构。我们将在接下来的文中更详细地探讨具体细节。
Items to take note of at this point are:
需要注意的几点是:
- All of the standard library classes are in the
std
namespace.- 标准库中的所有类都在
std
命名空间中。
- 标准库中的所有类都在
- The implementation details hinted at in this slide and elaborated later are not specified by the standard; we are illustrating one possible instantiation.
- 本文中暗示的实现细节以及后续详细说明的内容并未由标准指定;我们展示的是一种可能的实例化方式。
- Variables and methods related to exception handling are omitted.
- 省略了与异常处理相关的变量和方法。
ios_base Status Methods
ios_base
状态方法
class ios_base {
public:
bool good() const; // true if no error flag is set
bool eof() const; // true if eofbit is set
bool fail() const; // true if badbit or failbit are set
bool bad() const; // true if badbit is set
operator void*() const; // null pointer if fail(), non-null otherwise
void clear(iostate newstate = goodbit); // sets state to newstate
void setstate(iostate addstate); // adds addstate to existing state
enum iostate {
goodbit = 0x0000, // everything's ok
eofbit = 0x0001, // stream is at end of file
failbit = 0x0002, // last I/O operation failed
badbit = 0x0004 // serious error, stream unusable
};
protected:
unsigned long state; // stores status bits
};
Some useful code techniques can be illuminated with this information. For example:
利用这些信息可以展示一些有用的编码技巧。例如:
std::ifstream in;
in.open(filename);
while (!in) {
std::cout << "Cannot open file " << filename << "-try again:";
std::cin >> filename;
in.clear();
in.open(filename);
}
The implementation of setstate uses bitwise operations. These are native operations of C that allow direct access to integral values at the bit level. Most current CPUs have hardware support for bitwise operations, making them extremely efficient, typically requiring one clock cycle to accomplish the entire operation.
setstate
的实现使用了位运算。这些是 C 的原生操作,允许直接在位级别访问整数值。大多数现代 CPU 都有硬件支持位运算,这使得它们极其高效,通常只需要一个时钟周期即可完成整个操作。
Here is a table showing the bitwise operations and their semantics:
以下表格展示了位运算及其语义:
Operation | Symbol | Type | Infix Version | Accumulator Version |
---|---|---|---|---|
and | & | binary | z = x & y | z &= y |
or | | | binary | z = x | y | z |= y |
xor | ^ | binary | z = x ^ y | z ^= y |
not | ~ | unary | z = ~y | (na) |
left shift | << | binary | z = x << n | (na) |
right shift | >> | binary | z = x >> n | (na) |
ios_base Formatting Methods
ios_base
格式化方法
class ios_base {
public:
fmtflags flags() const; // returns current flags
fmtflags flags(fmtflags newflags); // sets flags to newflags
fmtflags setf(fmtflags setbits); // sets specified flags
fmtflags setf(fmtflags setbits, fmtflags mask); // sets flags in mask
fmtflags unsetf(fmtflags unsetbits); // clears specified flags
enum fmtflags {
boolalpha = 0x0001, // read/write bool values in alphabetic
left = 0x0002, // left-justify output
right = 0x0004, // right-justify output
internal = 0x0008, // prefix left..fill..number right
skipws = 0x0010, // skip white space before extraction
dec = 0x0020, // decimal
hex = 0x0040, // hexadecimal
oct = 0x0080, // octal
showbase = 0x0100, // show base indicator on output
showpoint = 0x0200, // show decimal point for fixed point output
showpos = 0x0400, // force show of sign for positive numbers
fixed = 0x0800, // force decimal notation for float
scientific = 0x1000, // force scientific notation for float
unitbuf = 0x2000, // flush buffer after each insertion
uppercase = 0x4000 // use upper case indicators for hex and e
};
protected:
unsigned long flags; // stores flag bits
};
Meaning of Format Flags
格式标志的含义
Format Flag | Meaning |
---|---|
boolalpha | 以字母形式读写布尔值 |
left | 左对齐输出 |
right | 右对齐输出 |
internal | 数字输出时,前缀、填充字符和数字分别对齐 |
skipws | 提取前跳过空白字符 |
dec | 十进制 |
hex | 十六进制 |
oct | 八进制 |
showbase | 在输出中显示基数指示符 |
showpoint | 在定点输出中显示小数点 |
showpos | 强制显示正数的符号 |
fixed | 强制浮点数以十进制形式输出 |
scientific | 强制浮点数以科学计数法输出 |
unitbuf | 每次插入后刷新缓冲区 |
uppercase | 在十六进制和科学计数法中使用大写字母 |
ios_base Data Methods
ios_base
数据方法
class ios_base {
public:
char fill(char fillch); // 设置填充字符
int precision() const; // 返回精度值
int precision(int val); // 设置精度值
int width() const; // 返回宽度值
int width(int val); // 设置宽度值
protected:
int width_value; // 初始化为 0
int precision_value; // 初始化为 0
char fill_character; // 初始化为空格
};
Meaning of Width and Precision
宽度和精度的含义
- Width:
n <= 0
: 无影响n > 0
: 在输出时,设置最小输出字符数(不足部分用填充字符填充)。在输入时,设置字符串提取的缓冲区长度。注意:每次插入和提取后宽度值会重置为 0。
- Precision:
n < 0
: 默认值n >= 0
: 如果设置了ios::fixed
,则确定小数点后显示的位数。如果没有设置ios::fixed
,则确定显示的有效数字位数。
ios_base Binding Methods
ios_base
绑定方法
class ios_base {
public:
streambuf* rdbuf(); // 返回指向流的 streambuf 对象的指针
ostream* tie(); // 返回指向绑定的 ostream 的指针
ostream* tie(ostream*); // 将当前流绑定到指定的 ostream
static bool sync_with_stdio(bool sync = true); // 与 C 标准 I/O 同步
protected:
streambuf* streambuffer; // 指向 streambuf 对象的指针
ostream* tied_ostream; // 指向 ostream 对象的指针
};
ios_base File Modes
ios_base
文件模式
class ios_base {
public:
enum open_mode {
in = 0x0001, // 以输入模式打开文件
out = 0x0002, // 以输出模式打开文件
ate = 0x0004, // 打开文件时定位到文件末尾
app = 0x0008, // 以追加模式打开文件
trunc = 0x0010, // 如果文件存在则截断文件
binary = 0x0020 // 以二进制模式打开文件
};
enum seek_dir {
beg = 0x0100, // 从文件开头开始定位
cur = 0x0200, // 从当前位置开始定位
end = 0x0400 // 从文件末尾开始定位
};
protected:
unsigned long mode; // 存储打开和定位模式的位
};
Meaning of File Modes
文件模式的含义
Open Mode | Effect | Effect with binary |
---|---|---|
in | 打开文本文件以供读取,初始位置在文件开头 | 初始位置在文件开头 |
out | 如果文件存在则将其截断为空,或创建文件以供写入 | 对空文件无影响 |
`out | trunc` | 截断文件为空,或创建文件以供写入 |
app | 以追加模式打开或创建文本文件,写入操作在文件末尾进行 | 无额外影响 |
`in | out` | 打开文件以供更新(读或写),位置在文件开头 |
`in | out | trunc` |
Possible ios_base Method Implementations
可能的 ios_base
方法实现
ios_base::fmtflags ios_base::flags() const {
return flags;
}
ios_base::fmtflags ios_base::flags(ios_base::fmtflags newflags) {
ios_base::fmtflags oldflags = flags;
flags = newflags;
return oldflags;
}
ios_base::fmtflags ios_base::setf(ios_base::fmtflags setbits) {
ios_base::fmtflags oldflags = flags;
flags |= setbits;
return oldflags;
}
ios_base::fmtflags ios_base::setf(ios_base::fmtflags setbits, ios_base::fmtflags mask) {
ios_base::fmtflags oldflags = flags;
flags = (flags & ~mask) | (setbits & mask);
return oldflags;
}
ios_base::fmtflags ios_base::unsetf(ios_base::fmtflags unsetbits) {
ios_base::fmtflags oldflags = flags;
flags &= ~unsetbits;
return oldflags;
}
char ios_base::fill(char newfill) {
char oldfill = fill_character;
fill_character = newfill;
return oldfill;
}
int ios_base::precision() const {
return precision_value;
}
int ios_base::precision(int val) {
int oldprecision = precision_value;
precision_value = val;
return oldprecision;
}
int ios_base::width() const {
return width_value;
}
int ios_base::width(int val) {
int oldwidth = width_value;
width_value = val;
return oldwidth;
}
Class istream
istream
类
namespace std {
class istream : public ios_base {
public:
friend istream& operator>>(istream&, char&);
friend istream& operator>>(istream&, int);
friend istream& operator>>(istream&, long);
friend istream& operator>>(istream&, unsigned char);
friend istream& operator>>(istream&, unsigned int);
friend istream& operator>>(istream&, unsigned long);
friend istream& operator>>(istream&, float);
friend istream& operator>>(istream&, double);
friend istream& operator>>(istream&, long double);
friend istream& operator>>(istream&, char*);
void get(char&);
char get();
char peek();
// 预定义对象
cin;
};
}
Class ostream
ostream
类
namespace std {
class ostream : public ios_base {
public:
friend ostream& operator<<(ostream&, char);
friend ostream& operator<<(ostream&, int);
friend ostream& operator<<(ostream&, long);
friend ostream& operator<<(ostream&, unsigned char);
friend ostream& operator<<(ostream&, unsigned int);
friend ostream& operator<<(ostream&, unsigned long);
friend ostream& operator<<(ostream&, float);
friend ostream& operator<<(ostream&, double);
friend ostream& operator<<(ostream&, long double);
friend ostream& operator<<(ostream&, const char*);
void put(char ch);
// 预定义对象
cout, cerr, clog;
};
}
Predefined Objects cin, cout, cerr, clog
预定义对象 cin
, cout
, cerr
, clog
- Predefined
istream
object:cin
- 预定义的
istream
对象:cin
- 预定义的
- Predefined
ostream
objects:cout
,cerr
,clog
- 预定义的
ostream
对象:cout
,cerr
,clog
- 预定义的
- Buffered
ostream
s:cout
,clog
- 缓冲的
ostream
:cout
,clog
- 缓冲的
- Tied stream pair:
cin
,cout
- 绑定的流对:
cin
,cout
- 绑定的流对:
cin.tie(&cout);
// in file iostreamcin.tie(&cout);
// 在文件 iostream 中
Class ifstream
ifstream
类
class ifstream : public istream {
public:
ifstream* open(const char* filename, ios_base::open_mode mode = ios_base::in);
ifstream* close();
};
Class ofstream
ofstream
类
class ofstream : public ostream {
public:
ofstream* open(const char* filename, ios_base::open_mode mode = ios_base::out | ios_base::trunc);
ofstream* close();
};
I/O Manipulators - 1
I/O 操纵符 - 1
boolalpha // calls s.setf(ios::boolalpha)
noboolalpha // calls s.unsetf(ios::boolalpha)
noshowbase // calls s.unsetf(ios::showbase)
showbase // calls s.setf(ios::showbase)
showpoint // calls s.setf(ios::showpoint)
noshowpoint // calls s.unsetf(ios::showpoint)
showpos // calls s.setf(ios::showpos)
noshowpos // calls s.unsetf(ios::showpos)
uppercase // calls s.setf(ios::uppercase)
nouppercase // calls s.unsetf(ios::uppercase)
skipws // calls s.setf(ios::skipws)
noskipws // calls s.unsetf(ios::skipws)
unitbuf // calls s.setf(ios::unitbuf)
nounitbuf // calls s.unsetf(ios::unitbuf)
left // calls s.setf(ios::left, ios::adjustfield)
right // calls s.setf(ios::right, ios::adjustfield)
internal // calls s.setf(ios::internal, ios::adjustfield)
dec // calls s.setf(ios::dec, ios::basefield)
hex // calls s.setf(ios::hex, ios::basefield)
oct // calls s.setf(ios::oct, ios::basefield)
fixed // calls s.setf(ios::fixed, ios::floatfield)
scientific // calls s.setf(ios::scientific, ios::floatfield)
endl // flushes streambuf and inserts '\n'
ends // flushes streambuf and inserts '\0'
flush // flushes streambuf
I/O Manipulators - 2
I/O 操纵符 - 2
Prototype for 0 - parameter manipulators:
stream_type& manip_name(stream_type& s);
// User can define and use manipulators, e.g.:
ostream& beepbeep(ostream& os) {
os << "lala";
return os;
}
I/O Manipulators - 3
I/O 操纵符 - 3
setbase(int b); // 设置数字的基数(进制)
setiosflags(ios::format_flags mask); // 调用 ios::setf(mask)
resetiosflags(ios::format_flags mask); // 调用 ios::unsetf(mask)
setfill(char ch); // 调用 ios::fill(ch)
setprecision(int n); // 调用 ios::precision(n)
setw(int n); // 调用 ios::width(n)
// 用户可以定义更广泛的操纵符集合
C++ 的 iostream 标准库介绍与使用详解
posted @ 2016-07-07 21:36 极客先锋
0 为何需要 iostream
在 C++ 编程中,输入输出操作是基础功能之一,而这些功能是由 iostream 库提供的。因此,深入理解 iostream 库的实现与使用是十分必要的。与 C 语言的 stdio 库不同,iostream 库是基于面向对象的设计理念,通过多重继承与虚拟继承构建的层次结构,并作为 C++ 标准库的一部分提供给程序员使用。
iostream 库为内置类型对象提供了输入输出支持,同时也支持文件的输入输出操作。此外,通过扩展 iostream 库,类的设计者可以为自定义类型添加输入输出支持。
以下通过一个示例说明为何需要扩展库来支持自定义类型的输入输出:
#include <stdio.h>
#include <iostream>
using namespace std;
class Test
{
public:
Test ( int a = 0 , int b = 0 )
{
this -> a = a;
this -> b = b;
}
int a;
int b;
};
int main()
{
Test t ( 100 , 50 );
printf ( "%???" , t ); // 输出格式不明确
scanf ( "%???" , t ); // 输入格式不明确
cout << t << endl; // 输出格式不明确
cin >> t; // 输入格式不明确
system ( "pause" );
}
由于自定义类的特殊性,上述代码中无论是使用 C 风格的输入输出,还是 C++ 的输入输出,都无法明确表示其格式。C 语言没有运算符重载机制,导致 stdio 库无法扩充以支持对自定义类对象的识别。而 C++ 通过运算符重载机制扩充 iostream 库,使系统能够识别自定义类型,从而明确输入输出的格式。
在上述示例中,printf
与 cout
的对比展示了 C 与 C++ 处理输入输出的根本区别:C 语言的输入输出是基于函数调用的方式,而 C++ 则是基于对象模式,cout
和 cin
分别是 ostream
类和 istream
类的对象。
1 iostream:istream 与 ostream
C++ 中的 iostream 库主要包含以下头文件:
IOSstream 库 | |
---|---|
fstream | iomanip |
ios | iosfwd |
iostream | istream |
ostream | sstream |
streambuf | strstream |
我们熟悉的输入输出操作分别由 istream
(输入流)和 ostream
(输出流)这两个类提供。为了支持双向输入输出操作,由 istream
和 ostream
派生出了 iostream
类。
类的继承关系如下图所示:
iostream 库定义了以下三个标准流对象:
cin
:表示标准输入(standard input)的istream
类对象,其默认输入设备为键盘。cout
:表示标准输出(standard output)的ostream
类对象,其默认输出设备为显示器屏幕。cerr
:表示标准错误(standard error)的ostream
类对象,其默认输出设备为显示器屏幕,仅用于输出错误信息。
输出主要由重载的左移操作符(<<
)完成,输入主要由重载的右移操作符(>>
)完成:
>> a
表示将数据输入到对象a
中。<< a
表示将对象a
中的数据输出。
这些标准流对象都有默认的设备,如下表所示:
C++ 对象名 | 设备名称 | C 中标准设备名 | 默认含义 |
---|---|---|---|
cin | 键盘 | stdin | 标准输入 |
cout | 显示器屏幕 | stdout | 标准输出 |
cerr | 显示器屏幕 | stderr | 标准错误输出 |
上表表明,cin
对象的默认输入设备是键盘,cout
对象的默认输出设备是显示器屏幕。
那么,C++ 是如何利用 cin
/cout
对象与左移和右移运算符重载来实现输入输出的呢?以下以输出为例,说明其实现原理:
cout
是ostream
类的对象,因为它所指向的是标准设备(显示器屏幕),所以它在 iostream 头文件中作为全局对象进行定义。ostream cout(stdout);
// 其默认指向的 C 中的标准设备名,作为其构造函数的参数使用。- 在 iostream 头文件中,
ostream
类对应每个基本数据类型都有其友元函数对左移操作符进行了重载。ostream& operator<<(ostream &temp, int source);
ostream& operator<<(ostream &temp, char *ps);
- … 等等
一句输出语句:cout << "http://www.cppblog.com/andxie99";
,事实上调用的就是 ostream& operator<<(ostream &temp, char *ps);
这个运算符重载函数。由于返回的是流对象的引用,引用可以作为左值使用,所以当程序中有类似 cout << "http://www.cppblog.com/andxie99" << "白纸人生";
这样的语句出现的时候,就能够构成连续输出。
由于 iostream 库不仅支持对象的输入输出,同时也支持文件流的输入输出,所以在详细讲解左移与右移运算符重载之前,我们有必要先对文件的输入输出以及输入输出的控制符有所了解。
2 fstream:ifstream 和 ofstream
与文件相关的输入输出类主要在 fstream 头文件中被定义。在这个头文件中主要定义了三个类,由这三个类控制对文件的各种输入输出操作,它们分别是 ifstream
、ofstream
、fstream
。其中 fstream
类是由 iostream
类派生而来,它们之间的继承关系如下图所示:
由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在 fstream 头文件中是没有像 cout
那样预先定义的全局对象。因此,我们必须自己定义一个该类的对象。如果要以文件作为设备向文件输出信息(也就是向文件写数据),那么就应该使用 ofstream
类。
ofstream
类的默认构造函数原型为:
ofstream::ofstream ( const char *filename , int mode = ios::out , int openprot = filebuf::openprot );
filename
:要打开的文件名。mode
:要打开文件的方式。openprot
:打开文件的属性。
其中 mode
和 openprot
这两个参数的可选项如下表所示:
mode 属性表 | |
---|---|
ios::app | 以追加的方式打开文件 |
ios::ate | 文件打开后定位到文件尾,ios::app 就包含有此属性 |
ios::binary | 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文 |
ios::in | 文件以输入方式打开 |
ios::out | 文件以输出方式打开 |
ios::trunc | 如果文件存在,把文件长度设为 0 |
可以用“或”把以上属性连接起来,例如 ios::out | ios::binary
。
openprot 属性表 | |
---|---|
属性 | 含义 |
0 | 普通文件,打开访问 |
1 | 只读文件 |
2 | 隐含文件 |
4 | 系统文件 |
可以用“或”或者“+”把以上属性连接起来,例如 3
或 1 | 2
,表示以只读和隐含属性打开文件。
实例代码如下:
#include <fstream>
using namespace std;
int main()
{
ofstream myfile ( "c:\\1.txt" , ios::out | ios::trunc , 0 );
myfile << "白纸人生" << endl << "网址:" << "www.cppblog.com/andxie99";
myfile.close();
system ( "pause" );
}
文件使用完后可以使用 close
成员函数关闭文件。
ios::app
为追加模式,在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。示例如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream myfile ( "c:\\1.txt" , ios::app , 0 );
if ( !myfile ) // 或者写成 myfile.fail()
{
cout << "文件打开失败,目标文件状态可能为只读!";
system ( "pause" );
exit ( 1 );
}
myfile << "白纸人生" << endl << "网址:" << "www.cppblog.com/andxie99" << endl;
myfile.close();
}
在定义 ifstream
和 ofstream
类对象的时候,我们也可以不指定文件。以后可以通过成员函数 open()
显式地把一个文件连接到一个类对象上。例如:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream myfile;
myfile.open ( "c:\\1.txt" , ios::out | ios::app , 0 );
if ( !myfile ) // 或者写成 myfile.fail()
{
cout << "文件创建失败,磁盘不可写或者文件为只读!";
system ( "pause" );
exit ( 1 );
}
myfile << "白纸人生" << endl << "网址:" << "www.cppblog.com/andxie99" << endl;
myfile.close();
}
以下是一个利用 ifstream
类对象,将文件中的数据读取出来,然后再输出到标准设备中的例子。代码如下:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream myfile;
myfile.open ( "c:\\1.txt" , ios::in , 0 );
if ( !myfile )
{
cout << "文件读错误";
system ( "pause" );
exit ( 1 );
}
char ch;
string content;
while ( myfile.get ( ch ) )
{
content += ch;
cout.put ( ch ); // cout << ch; 这么写也是可以的
}
myfile.close();
cout << content;
system ( "pause" );
}
在上述代码中,我们利用成员函数 get()
,逐一读取文件中的有效字符,再利用 put()
成员函数,将文件中的数据通过循环逐一输出到标准设备(屏幕)上。get()
成员函数会在文件读到末尾的时候返回假值,所以我们可以利用它的这个特性作为 while
循环的终止条件。同时,我们在上述代码中引入了 C++ 风格的字符串类型 string
,在循环读取的时候逐一保存到 content
中。要使用 string
类型,必须包含 <string>
头文件。
在简单介绍过 ofstream
类和 ifstream
类后,再来看一下 fstream
类。fstream
类是由 iostream
派生而来,fstream
类对象可以同时对文件进行读写操作。
示例代码如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream myfile;
myfile.open ( "c:\\1.txt" , ios::out | ios::app , 0 );
if ( !myfile )
{
cout << "文件写错误,文件属性可能为只读!" << endl;
system ( "pause" );
exit ( 1 );
}
myfile << "白纸人生" << endl << "网址:" << "www.cppblog.com/andxie99" << endl;
myfile.close();
myfile.open ( "c:\\1.txt" , ios::in , 0 );
if ( !myfile )
{
cout << "文件读错误,文件可能丢失!" << endl;
system ( "pause" );
exit ( 1 );
}
char ch;
while ( myfile.get ( ch ) )
{
cout.put ( ch );
}
myfile.close();
system ( "pause" );
}
由于 fstream
类可以对文件同时进行读写操作,所以对其对象进行初始化的时候一定要显式地指定 mode
和 openprot
参数。
3 strstream:ostrstream 与 istrstream
简单理解就是能够控制字符串类型对象进行输入输出的类。C++ 不仅可以支持 C++ 风格的字符串流控制,还可以支持 C 风格的字符串流控制。
我们先看看 C++ 是如何对 C 风格的字符串流进行控制的。C 中的字符串其实就是字符数组,字符数组内的数据在内存中的位置是连续的。我们通常用 char str[size]
或者 char *str
的方式声明创建 C 风格字符数组。为了能让字符数组作为设备并提供输入输出操作,C++ 引入了 ostrstream
、istrstream
、strstream
这三个类。要使用它们创建对象,就必须包含 <strstream>
头文件。
istrstream
类用于执行 C 风格的串流的输入操作,也就是以字符串数组作为输入设备。ostrstream
类用于执行 C 风格的串流的输出操作,也就是以字符串数组作为输出设备。strstream
类同时可以支持 C 风格的串流的输入输出操作。
istrstream
类是从 istream
(输入流类)和 strstreambase
(字符串流基类)派生而来,ostrstream
是从 ostream
(输出流类)和 strstreambase
(字符串流基类)派生而来,strstream
则是从 iostream
(输入输出流类)和 strstreambase
(字符串流基类)派生而来。
它们的继承关系如下图所示:
串流同样不是标准设备,不会有预先定义好的全局对象,所以不能直接操作,需要通过构造函数创建对象。
istrstream
类的构造函数原型如下:
istrstream::istrstream ( const char *str , int size );
参数 1 表示字符串数组,而参数 2 表示数组大小。当 size
为 0 时,表示 istrstream
类对象直接连接到由 str
所指向的内存空间并以 \0
结尾的字符串。
以下是一个利用 istrstream
类创建对象,指定流输入设备为字符串数组,通过它向一个字符型对象输入数据的示例代码:
#include <iostream>
#include <strstream>
#include <cstring> // 包含 strlen 函数
using namespace std;
int main()
{
char *name = "www.cppblog.com/andxie99";
int arraysize = strlen ( name ) + 1;
istrstream is ( name , arraysize );
char temp;
is >> temp;
cout << temp;
system ( "pause" );
}
ostrstream
类用于执行串流的输出,它的构造函数如下所示:
ostrstream::ostrstream ( char *_Ptr , int streamsize , int Mode = ios::out );
第一个参数是字符数组,第二个是说明数组的大小,第三个参数是指打开方式。
以下是一个示例代码:
#include <iostream>
#include <strstream>
using namespace std;
int main()
{
int arraysize = 100;
char *pbuffer = new char [ arraysize ];
ostrstream ostr ( pbuffer , arraysize , ios::out );
ostr << arraysize << ends; // 使用 ostrstream 输出到流对象的时候,要用 ends 结束字符串
cout << pbuffer;
delete[] pbuffer;
system ( "pause" );
}
在上述代码中,我们创建一个 C 风格的串流输出对象 ostr
,将 arraysize
内的数据成功地以字符串的形式输出到了 ostr
对象所指向的 pbuffer
指针的堆空间中。pbuffer
正是我们要输出的字符串数组。在结尾要使用 ends
结束字符串,否则会有溢出的危险。
4 stringstream
stringstream
用于 C++ 风格的字符串的输入输出。stringstream
的构造函数原型如下:
stringstream::stringstream ( string str );
示例代码如下:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
stringstream ostr ( "ccc" );
ostr.put ( 'd' );
ostr.put ( 'e' );
ostr << "fg";
string gstr = ostr.str();
cout << gstr << endl;
char a;
ostr >> a;
cout << a;
system ( "pause" );
}
此外,stringstream
类的对象还常用于 string
与各种内置类型数据之间的转换。示例代码如下:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
stringstream sstr;
//--------int 转 string-----------
int a = 100;
string str;
sstr << a;
sstr >> str;
cout << str << endl;
//--------string 转 char[]--------
sstr.clear(); // 如果你想通过使用同一 stringstream 对象实现多种类型的转换,请注意在每一次转换之后都必须调用 clear() 成员函数
string name = "colinguan";
char cname [ 200 ];
sstr << name;
sstr >> cname;
cout << cname;
system ( "pause" );
}
5 io_state 输入 / 输出的状态标志
C++ 中负责输入 / 输出的系统包括了关于每一个输入 / 输出操作的结果的记录信息。这些当前的状态信息被包含在 io_state
类型的对象中。io_state
是一个枚举类型,以下便是它包含的值:
goodbit
:无错误eofbit
:已到达文件尾failbit
:非致命的输入 / 输出错误,可挽回badbit
:致命的输入 / 输出错误,无法挽回
有两种方法可以获得输入 / 输出的状态信息。一种方法是通过调用 rdstate()
函数,它将返回当前状态的错误标记。例如,如果没有错误,则 rdstate()
会返回 goodbit
。示例代码如下:
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
cout << cin.rdstate() << endl;
if ( cin.rdstate() == ios::goodbit )
{
cout << "输入数据的类型正确,无错误!" << endl;
}
if ( cin.rdstate() == ios_base::failbit )
{
cout << "输入数据类型错误,非致命错误,可清除输入缓冲区挽回!" << endl;
}
system ( "pause" );
}
另一种方法则是使用以下任何一个函数来检测相应的输入 / 输出状态:
bool bad();
bool eof();
bool fail();
bool good();
示例代码如下:
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
cout << cin.rdstate() << endl;
if ( cin.good() )
{
cout << "输入数据的类型正确,无错误!" << endl;
}
if ( cin.fail() )
{
cout << "输入数据类型错误,非致命错误,可清除输入缓冲区挽回!" << endl;
}
system ( "pause" );
}
如果错误发生,那么流状态既被标记为错误,你必须清除这些错误状态,以使你的程序能正确适当地继续运行。要清除错误状态,需使用 clear()
函数。此函数带一个参数,它是你将要设为当前状态的标志值。通常,只要将 ios::goodbit
作为实参即可。
示例代码如下:
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
cout << cin.rdstate() << endl;
cin.clear ( ios::goodbit );
cout << cin.rdstate() << endl;
system ( "pause" );
}
通常,当我们发现输入有错又需要改正的时候,使用 clear()
更改标记为正确后,同时也需要使用 get()
成员函数清除输入缓冲区,以达到重复输入的目的。示例代码如下:
#include <iostream>
using namespace std;
int main()
{
int a;
while ( 1 )
{
cin >> a;
if ( !cin ) // 条件可改写为 cin.fail()
{
cout << "输入有错!请重新输入" << endl;
cin.clear();
cin.get();
}
else
{
cout << a;
break;
}
}
system ( "pause" );
}
最后再给出一个对文件流错误标记处理的例子,巩固学习。代码如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream myfile ( "c:\\1.txt" , ios_base::in , 0 );
if ( myfile.fail() )
{
cout << "文件读取失败或指定文件不存在!" << endl;
}
else
{
char ch;
while ( myfile.get ( ch ) )
{
cout << ch;
}
if ( myfile.eof() )
{
cout << "文件内容已经全部读完" << endl;
}
}
system ( "pause" );
}
C++ 中头文件 iostream 介绍
fengbingchun 于 2017-03-19 16:13:04 发布
1. C++ 输入输出机制概述
C++ 语言并不直接处理输入输出,而是通过定义在标准库中的一簇类型来实现输入输出操作。这些类型支持从设备读取数据以及向设备写入数据,设备可以是文件、控制台窗口等。此外,还有一些类型允许内存输入输出,即从 string
读取数据或向 string
写入数据。
2. 头文件 iostream 的作用
在 C++/C++11 中,头文件 <iostream>
定义了标准输入/输出流对象。包含 <iostream>
时,也会自动包含 <ios>
、<streambuf>
、<istream>
、<ostream>
和 <iosfwd>
。
3. iostream 中的对象
3.1 窄字符(char)
cin
:标准输入流(对象)。cout
:标准输出流(对象)。cerr
:标准错误输出流(对象),用于输出错误信息。clog
:标准日志输出流(对象),用于记录日志信息。
3.2 宽字符(wchar_t)
wcin
:标准输入流(宽字符)(对象)。wcout
:标准输出流(宽字符)(对象)。wcerr
:标准错误输出流(宽字符)(对象)。wclog
:标准日志输出流(宽字符)(对象)。
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 istream cin, *_Ptr_cin;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cout, *_Ptr_cout;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cerr, *_Ptr_cerr;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream clog, *_Ptr_clog;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 wistream wcin, *_Ptr_wcin;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 wostream wcout, *_Ptr_wcout;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 wostream wcerr, *_Ptr_wcerr;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 wostream wclog, *_Ptr_wclog;
4. IO 库的组成
4.1 输入输出流类型
istream
:输入流类型,提供输入操作。ostream
:输出流类型,提供输出操作。
4.2 标准输入输出流对象
cin
:一个istream
对象,标准输入流,用于从标准输入读取数据。cout
:一个ostream
对象,标准输出流,用于向标准输出写入数据。输出可以重定向(使用 “>` 或 “1>”)到指定文件中,通常用于程序的正常输出内容。cerr
:一个ostream
对象,标准错误流,用于输出程序错误信息或其他不属于正常逻辑的输出内容。写入到cerr
的数据默认不经过缓冲区,直接输出到显示器,可以通过 “2>” 重定向到指定文件。clog
:一个ostream
对象,标准日志流,关联到标准错误。与cerr
的区别在于,cerr
的输出不经过缓冲区,而clog
的输出默认会存放在缓冲区,缓冲区满或遇到endl
时才会输出。clog
通常用于报告程序的执行信息,可存入日志文件。
4.3 输入输出操作符
>>
:从istream
对象读取输入数据的操作符。<<
:向ostream
对象写入输出数据的操作符。
4.4 其他相关函数
getline
:从给定的istream
读取一行数据,并存入给定的string
对象中。
5. IO 库类型与头文件的关系
<iostream>
:定义了用于读写流的基本类型。<fstream>
:定义了读写命名文件的类型。<sstream>
:定义了读写内存string
对象的类型。
6. 宽字符支持
为了支持使用宽字符的语言,标准库定义了一组类型和对象来操作 wchar_t
类型的数据。宽字符版本的类型和函数的名字以字母 “w” 开始,例如,wcin
、wcout
和 wcerr
分别是 cin
、cout
和 cerr
的宽字符版本。宽字符版本的类型和对象与其对应的普通 char
版本的类型定义在同一个头文件中。
7. 测试代码
7.1 使用标准输出流 cout
#include <iostream>
int test_iostream_cout()
{
char str[] = "Hello C++";
std::cout << "Value of str is : " << str << std::endl;
return 0;
}
7.2 使用标准输入流 cin
#include <iostream>
int test_iostream_cin()
{
char name[50];
std::cout << "Please enter your name: ";
std::cin >> name;
std::cout << "Your name is: " << name << std::endl;
return 0;
}
7.3 使用标准日志流 clog
#include <iostream>
int test_iostream_clog()
{
char str[] = "Unable to read....";
std::clog << "Error message : " << str << std::endl;
return 0;
}
7.4 使用标准错误流 cerr
#include <iostream>
int test_iostream_cerr()
{
char str[] = "Unable to read....";
std::cerr << "Error message : " << str << std::endl;
return 0;
}
7.5 宽字符版本的输入输出
#include <iostream>
static void TestWide()
{
int i = 0;
std::wcout << L"Enter a number: ";
std::wcin >> i;
std::wcerr << L"test for wcerr" << std::endl;
std::wclog << L"test for wclog" << std::endl;
}
int test_iostream_w()
{
int i = 0;
std::cout << "Enter a number: ";
std::cin >> i;
std::cerr << "test for cerr" << std::endl;
std::clog << "test for clog" << std::endl;
TestWide();
return 0;
}
8. iostream 的本质
iostream
是 C++ 标准库的一个头文件。标准库的 “标准” 之意在于,每个 C++ 编译器都必须自带该库,无论使用何种 C++ 编译器,其用法和行为都是一致的。头文件中仅包含标准库中类和函数的声明,而标准库的实际代码是预编译的(或者是类模板,这种情况下会直接写在头文件中)。通过 #include
该头文件,告知编译器使用其中声明的内容,编译器则负责将实际的库函数与代码一起生成可执行文件。
头文件 <iostream>
本身没有扩展名,它会进一步包含其他头文件。通过逐层查找,可以了解 cin
和 cout
等对象的具体实现。
9. IO 的广泛含义
在计算机领域,IO 通常指输入(Input)和输出(Output),其含义较为广泛,不仅限于 iostream
,而是泛指计算机的输入和输出操作。
C++ 中的 iostream 标准库
瑞 新 于 2019-03-28 10:18:43 发布
1. iostream 标准库概述
C++ 提供了一个强大的输入输出(Input/Output,简称 I/O)标准库,用于处理各种输入和输出操作。该库的核心是 iostream
,其名称由 “i-o-stream” 组成,意为输入输出流。iostream
标准库是 C++ 标准库的一部分,提供了一套面向对象的输入输出机制,支持对标准输入输出设备(如键盘和显示器)以及文件的读写操作。
2. iostream 类库的层次结构
iostream
类库的层次结构基于面向对象的继承机制,其核心类包括:
ios
:抽象基类,定义了输入输出流的基本接口和状态管理功能。istream
:从ios
派生,支持输入操作。ostream
:从ios
派生,支持输出操作。iostream
:通过多重继承从istream
和ostream
派生,支持双向输入输出操作。
此外,为了支持文件的输入输出操作,iostream
类库还提供了以下类:
ifstream
:从istream
派生,用于从文件中读取数据。ofstream
:从ostream
派生,用于向文件中写入数据。fstream
:从iostream
派生,支持对文件的双向输入输出操作。
3. 使用 iostream 标准库
在 C++ 程序中,要使用 iostream
标准库的功能,需要在程序开头包含相应的头文件:
#include <iostream>
#include
是一个预处理指令,用于将指定的头文件内容插入到当前文件中。<iostream>
是 C++ 标准库中的一个头文件,它声明了 iostream
类库中所有类和函数的接口。
3.1 标准输入输出
iostream
标准库提供了两个全局对象,用于处理标准输入和输出:
cin
:istream
类的对象,用于从标准输入设备(通常是键盘)读取数据。cout
:ostream
类的对象,用于向标准输出设备(通常是显示器)写入数据。
3.2 文件输入输出
对于文件操作,可以使用以下类:
ifstream
:用于打开文件并从中读取数据。ofstream
:用于打开文件并向其中写入数据。fstream
:用于打开文件并进行双向读写操作。
3.3 标准库的特性
iostream
标准库具有以下特性:
- 面向对象:通过类和对象的形式,提供了一套统一的输入输出接口。
- 类型安全:支持对各种数据类型的输入输出操作,并通过重载运算符(如
<<
和>>
)实现类型安全的输入输出。 - 可扩展性:用户可以通过继承和重载机制,扩展
iostream
类库的功能,以支持自定义类型的输入输出。
C++——IOStream
鹅一百已于 2025-04-14 15:01:08 修改
1. 什么是 IO?
在 C 语言和 C++ 中,我们已经接触到了两种 IO(输入/输出)的概念。例如,以下代码分别展示了 C 语言和 C++ 中的 IO 库:
#include <stdio.h> // C 语言标准输入输出库
#include <iostream> // C++ 输入输出流库
iostream
是 C++ 中的 IO 流库,其中 I 代表输入(Input),O 代表输出(Output),用于实现用户与程序之间的交互。在早期的 C++ 程序中,我们通常使用固定的测试用例来验证程序结果,而较少关注输入输出的动态交互。然而,在实际应用中,大多数程序需要与用户进行实时交互,因此 IO 流是程序设计中不可或缺的部分。
2. 什么是流?
流(Stream)是一种连续且具有方向的概念,类似于水流或电流。在 IO 流中,输入(In)是指用户向内存输入数据,而输出(Out)是指程序将数据从内存输出到终端或其他设备。尽管流是连续的,但数据的捕获过程并非如此。用户可以持续向内存输入数据,但程序只会根据需要捕获特定的数据片段,而未捕获的数据则暂时存储在缓冲区中,等待下一次程序的读取。
3. C++ 中的 IO 流
在 C 语言中,最常用的 IO 函数是 printf
和 scanf
,它们分别用于输出和输入。然而,C++ 引入了 cin
和 cout
等新的 IO 流对象,以替代传统的 C 语言 IO 函数。C 语言的 IO 函数需要使用特定的格式化占位符来指定数据类型,这在处理复杂数据时容易出错,尤其是在涉及泛型编程时,数据类型可能不明确,导致格式化错误。
为了解决这一问题,C++ 利用面向对象的特性重新实现了 IO 流。其最大的改进是能够自动识别数据类型,无需使用占位符来指定数据类型。虽然 C++ 的 IO 流在某些情况下可能不如 C 语言的 printf
灵活,但 C++ 的 IO 流提供了更高的安全性和易用性。此外,C++ 并未禁用 printf
和 scanf
,在需要时仍可使用。
3.1 标准 IO 流
C++ 标准库提供了四个全局的 IO 流对象:
cin
:标准输入流,用于从键盘输入数据到内存。cout
:标准输出流,用于将数据从内存输出到控制台。cerr
:标准错误输出流,用于输出错误信息。clog
:标准日志输出流,用于记录日志信息。
其中,cin
的输入并非直接从键盘获取,而是通过缓冲区进行。用户输入的数据首先存储在缓冲区中,cin
从缓冲区中提取所需的数据,未提取的数据仍保留在缓冲区中,等待下一次读取。此外,当输入对象为字符或字符串时,空格和回车符无法通过 cin
输入,因为它们被用作分隔符,表示输入数据的结束。
在实际编程中,我们经常遇到以下代码:
while (cin >> a) {
// ...
}
这段代码的循环终止条件是如何判断的呢?我们知道,流插入操作(如 cin >> a
)的返回值是一个 istream
对象,而流提取操作的返回值是一个 ostream
对象。编译器无法直接将这些对象转换为布尔值,因此 C++ 标准库引入了一种新的语法——类型转换运算符重载。
3.2 类型转换运算符重载
类型转换运算符重载允许程序员自定义对象在特定类型转换时的行为。例如,以下代码展示了如何通过重载 operator bool()
来改变对象的布尔值判断逻辑:
class A {
public:
A(int a) : _a(a) {}
operator bool() {
if (_a > 10)
return false;
else
return true;
}
private:
int _a;
};
int main() {
A a1(20);
A a2(1);
cout << (bool)a1 << endl; // 输出:false
cout << (bool)a2 << endl; // 输出:true
}
通过重载 operator bool()
,我们改变了对象在布尔上下文中的行为。这种机制不仅适用于布尔类型,还可以用于其他类型的转换,从而大大提高程序的灵活性。
3.3 文件 IO 流
除了标准输入输出流,C++ 还提供了文件 IO 流,用于处理文件的读写操作。文件 IO 流包含三个主要对象:
ifstream
:用于文件输入(读取)。ofstream
:用于文件输出(写入)。fstream
:用于同时支持文件输入和输出。
文件的读写有两种方式:二进制文件和文本文件。二进制文件类似于浅拷贝,直接将数据的二进制形式原封不动地写入文件;而文本文件类似于深拷贝,只将有效数据以文本形式写入文件。
例如,对于一个单链表,如果使用二进制文件存储,则会直接复制链表节点的二进制数据,包括指针的值。然而,由于程序每次运行时的内存地址不同,这种存储方式会导致指针失效,读取到的数据可能无效。而使用文本文件存储时,需要手动遍历链表节点,将节点的值以字符串形式写入文件,并在读取时手动将字符串转换为合适的数据类型,重新构建链表。
文本文件操作的函数在不同库中可能存在差异,因此在需要时应查阅相关文档或自行实现。
C++ iostream、ostream、istream 等标准库详解
YoungGeeker 于 2022-08-28 19:04:46 发布
在编写 C++ 代码时,iostream
库是几乎每个程序都会用到的标准库,尽管有些人可能会选择使用 cstdio
。当我们深入查看 iostream
库的源码时,会发现其中包含大量的 include
、预处理、extern
和 namespace
等内容,同时还引入了 ios
、ostream
、istream
和 streambuf
等头文件。本文将为您详细揭秘这些内容。
iostream
库
iostream
是 C++ 的标准输入输出流库,其名称由 “输入(in)”、“输出(out)” 和 “流(stream)” 组合而成。
组成
iostream
库的基础是两种类型:istream
和 ostream
,分别表示输入流和输出流。流是指从某种 I/O 设备上读取或写入的字符序列,强调字符是随着时间顺序生成或消耗的。
标准库定义了 4 个 IO 对象:
cin
(istream
类型):标准输入。cout
(ostream
类型):标准输出。cerr
和clog
(ostream
类型):分别用于输出错误信息和程序执行的一般信息。
基本类模板
iostream
库基于类模板的层级结构,以一种与类型无关的方式提供大部分功能。基本类模板包含两个参数:字符类型(charT
)决定处理的元素类型,而特性参数为每个特定的元素类型提供额外的特征。
类层级结构中的类模板实例名称通常在类名前带有 basic_
前缀,例如:
istream
对应的类模板为basic_istream
。fstream
对应的类模板为basic_fstream
。
唯一的例外是 ios_base
,它本身是类型无关的,因此不是一个类模板,而是一个普通类。
类模板实例
iostream
库中包含两组标准的类模板层级结构实例:
- 面向单字节(
char
类型)的实例,例如ios
、istream
和ofstream
。 - 面向宽字节(
wchar_t
类型)的实例,其命名规则与单字节实例相同,但所有类和对象名称前带有w
前缀,例如wios
、wistream
和wofstream
。
标准对象
作为 iostream
库的一部分,头文件声明了一些用于标准输入输出设备的对象。这些对象分为两组:
- 面向单字节的对象:
cin
、cout
、cerr
和clog
。 - 面向宽字节的对象:
wcin
、wcout
、wcerr
和wclog
。
类型
iostream
库中的类很少直接使用基本类型作为成员的原型,而是通常使用根据其实例的特性定义的类型。对于默认的 char
和 wchar_t
类型的实例,streampos
、streamoff
和 streamsize
分别用于表示位置、偏移和大小。
操纵符
操纵符是与流对象的插入(<<
)和提取(>>
)运算符一同使用的全局函数,用于变更流的属性和格式设置。例如,endl
、hex
和 scientific
是常见的操纵符。
源码
以下是 iostream
库的源码:
// -*- C++ -*-
//===--------------------------- iostream ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP_IOSTREAM
#define _LIBCPP_IOSTREAM
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
namespace std {
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;
} // std
#include <__config>
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#ifndef _LIBCPP_HAS_NO_STDIN
extern _LIBCPP_FUNC_VIS istream cin;
extern _LIBCPP_FUNC_VIS wistream wcin;
#endif
#ifndef _LIBCPP_HAS_NO_STDOUT
extern _LIBCPP_FUNC_VIS ostream cout;
extern _LIBCPP_FUNC_VIS wostream wcout;
#endif
extern _LIBCPP_FUNC_VIS ostream cerr;
extern _LIBCPP_FUNC_VIS wostream wcerr;
extern _LIBCPP_FUNC_VIS ostream clog;
extern _LIBCPP_FUNC_VIS wostream wclog;
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_IOSTREAM
ostream
库
ostream
是专为窄字节设计的输出库。以下是其源码:
// Output streams -*- C++ -*-
// Copyright (C) 1997-2019 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file include/ostream
* This is a Standard C++ Library header.
*/
//
// ISO C++ 14882: 27.6.2 Output streams
//
#ifndef _GLIBCXX_OSTREAM
#define _GLIBCXX_OSTREAM 1
#pragma GCC system_header
#include <ios>
#include <bits/ostream_insert.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* @brief Template class basic_ostream.
* @ingroup io
*
* @tparam _CharT Type of character stream.
* @tparam _Traits Traits for character type, defaults to
* char_traits<_CharT>.
*
* This is the base class for all output streams. It provides text
* formatting of all builtin types, and communicates with any class
* derived from basic_streambuf to do the actual output.
*/
template<typename _CharT, typename _Traits>
class basic_ostream : virtual public basic_ios<_CharT, _Traits>
{
public:
// Types (inherited from basic_ios):
typedef _CharT char_type;
typedef typename _Traits::int_type int_type;
typedef typename _Traits::pos_type pos_type;
typedef typename _Traits::off_type off_type;
typedef _Traits traits_type;
// Non-standard Types:
typedef basic_streambuf<_CharT, _Traits> __streambuf_type;
typedef basic_ios<_CharT, _Traits> __ios_type;
typedef basic_ostream<_CharT, _Traits> __ostream_type;
typedef num_put<_CharT, ostreambuf_iterator<_CharT, _Traits> >
__num_put_type;
typedef ctype<_CharT> __ctype_type;
/**
* @brief Base constructor.
*
* This ctor is almost never called by the user directly, rather from
* derived classes' initialization lists, which pass a pointer to
* their own stream buffer.
*/
explicit
basic_ostream(__streambuf_type* __sb)
{ this->init(__sb); }
/**
* @brief Base destructor.
*
* This does very little apart from providing a virtual base dtor.
*/
virtual
~basic_ostream() { }
/// Safe prefix/suffix operations.
class sentry;
friend class sentry;
//@{
/**
* @brief Interface for manipulators.
*
* Manipulators such as @c std::endl and @c std::hex use these
* functions in constructs like "std::cout << std::endl". For more
* information, see the iomanip header.
*/
__ostream_type&
operator<<(__ostream_type& (*__pf)(__ostream_type&))
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 60. What is a formatted input function?
// The inserters for manipulators are *not* formatted output functions.
return __pf(*this);
}
__ostream_type&
operator<<(__ios_type& (*__pf)(__ios_type&))
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 60. What is a formatted input function?
// The inserters for manipulators are *not* formatted output functions.
__pf(*this);
return *this;
}
__ostream_type&
operator<<(ios_base& (*__pf) (ios_base&))
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 60. What is a formatted input function?
// The inserters for manipulators are *not* formatted output functions.
__pf(*this);
return *this;
}
//@}
//@{
/**
* @name Inserters
*
* All the @c operator<< functions (aka <em>formatted output
* functions</em>) have some common behavior. Each starts by
* constructing a temporary object of type std::basic_ostream::sentry.
* This can have several effects, concluding with the setting of a
* status flag; see the sentry documentation for more.
*
* If the sentry status is good, the function tries to generate
* whatever data is appropriate for the type of the argument.
*
* If an exception is thrown during insertion, ios_base::badbit
* will be turned on in the stream's error state without causing an
* ios_base::failure to be thrown. The original exception will then
* be rethrown.
*/
//@{
/**
* @brief Integer arithmetic inserters
* @param __n A variable of builtin integral type.
* @return @c *this if successful
*
* These functions use the stream's current locale (specifically, the
* @c num_get facet) to perform numeric formatting.
*/
__ostream_type&
operator<<(long __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(unsigned long __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(bool __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(short __n);
__ostream_type&
operator<<(unsigned short __n)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 117. basic_ostream uses nonexistent num_put member functions.
return _M_insert(static_cast<unsigned long>(__n));
}
__ostream_type&
operator<<(int __n);
__ostream_type&
operator<<(unsigned int __n)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 117. basic_ostream uses nonexistent num_put member functions.
return _M_insert(static_cast<unsigned long>(__n));
}
#ifdef _GLIBCXX_USE_LONG_LONG
__ostream_type&
operator<<(long long __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(unsigned long long __n)
{ return _M_insert(__n); }
#endif
//@}
//@{
/**
* @brief Floating point arithmetic inserters
* @param __f A variable of builtin floating point type.
* @return @c *this if successful
*
* These functions use the stream's current locale (specifically, the
* @c num_get facet) to perform numeric formatting.
*/
__ostream_type&
operator<<(double __f)
{ return _M_insert(__f); }
__ostream_type&
operator<<(float __f)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 117. basic_ostream uses nonexistent num_put member functions.
return _M_insert(static_cast<double>(__f));
}
__ostream_type&
operator<<(long double __f)
{ return _M_insert(__f); }
//@}
/**
* @brief Pointer arithmetic inserters
* @param __p A variable of pointer type.
* @return @c *this if successful
*
* These functions use the stream's current locale (specifically, the
* @c num_get facet) to perform numeric formatting.
*/
__ostream_type&
operator<<(const void* __p)
{ return _M_insert(__p); }
#if __cplusplus >= 201703L
__ostream_type&
operator<<(nullptr_t)
{ return *this << "nullptr"; }
#endif
/**
* @brief Extracting from another streambuf.
* @param __sb A pointer to a streambuf
*
* This function behaves like one of the basic arithmetic extractors,
* in that it also constructs a sentry object and has the same error
* handling behavior.
*
* If @p __sb is NULL, the stream will set failbit in its error state.
*
* Characters are extracted from @p __sb and inserted into @c *this
* until one of the following occurs:
*
* - the input stream reaches end-of-file,
* - insertion into the output sequence fails (in this case, the
* character that would have been inserted is not extracted), or
* - an exception occurs while getting a character from @p __sb, which
* sets failbit in the error state
*
* If the function inserts no characters, failbit is set.
*/
__ostream_type&
operator<<(__streambuf_type* __sb);
//@}
//@{
/**
* @name Unformatted Output Functions
*
* All the unformatted output functions have some common behavior.
* Each starts by constructing a temporary object of type
* std::basic_ostream::sentry. This has several effects, concluding
* with the setting of a status flag; see the sentry documentation
* for more.
*
* If the sentry status is good, the function tries to generate
* whatever data is appropriate for the type of the argument.
*
* If an exception is thrown during insertion, ios_base::badbit
* will be turned on in the stream's error state. If badbit is on in
* the stream's exceptions mask, the exception will be rethrown
* without completing its actions.
*/
/**
* @brief Simple insertion.
* @param __c The character to insert.
* @return *this
*
* Tries to insert @p __c.
*
* @note This function is not overloaded on signed char and
* unsigned char.
*/
__ostream_type&
put(char_type __c);
/**
* @brief Core write functionality, without sentry.
* @param __s The array to insert.
* @param __n Maximum number of characters to insert.
*/
void
_M_write(const char_type* __s, streamsize __n)
{
const streamsize __put = this->rdbuf()->sputn(__s, __n);
if (__put != __n)
this->setstate(ios_base::badbit);
}
/**
* @brief Character string insertion.
* @param __s The array to insert.
* @param __n Maximum number of characters to insert.
* @return *this
*
* Characters are copied from @p __s and inserted into the stream until
* one of the following happens:
*
* - @p __n characters are inserted
* - inserting into the output sequence fails (in this case, badbit
* will be set in the stream's error state)
*
* @note This function is not overloaded on signed char and
* unsigned char.
*/
__ostream_type&
write(const char_type* __s, streamsize __n);
//@}
/**
* @brief Synchronizing the stream buffer.
* @return *this
*
* If @c rdbuf() is a null pointer, changes nothing.
*
* Otherwise, calls @c rdbuf()->pubsync(), and if that returns -1,
* sets badbit.
*/
__ostream_type&
flush();
/**
* @brief Getting the current write position.
* @return A file position object.
*
* If @c fail() is not false, returns @c pos_type(-1) to indicate
* failure. Otherwise returns @c rdbuf()->pubseekoff(0,cur,out).
*/
pos_type
tellp();
/**
* @brief Changing the current write position.
* @param __pos A file position object.
* @return *this
*
* If @c fail() is not true, calls @c rdbuf()->pubseekpos(pos). If
* that function fails, sets failbit.
*/
__ostream_type&
seekp(pos_type);
/**
* @brief Changing the current write position.
* @param __off A file offset object.
* @param __dir The direction in which to seek.
* @return *this
*
* If @c fail() is not true, calls @c rdbuf()->pubseekoff(off,dir).
* If that function fails, sets failbit.
*/
__ostream_type&
seekp(off_type, ios_base::seekdir);
protected:
basic_ostream()
{ this->init(0); }
#if __cplusplus >= 201103L
// Non-standard constructor that does not call init()
basic_ostream(basic_iostream<_CharT, _Traits>&) { }
basic_ostream(const basic_ostream&) = delete;
basic_ostream(basic_ostream&& __rhs)
: __ios_type()
{ __ios_type::move(__rhs); }
// 27.7.3.3 Assign/swap
basic_ostream& operator=(const basic_ostream&) = delete;
basic_ostream&
operator=(basic_ostream&& __rhs)
{
swap(__rhs);
return *this;
}
void
swap(basic_ostream& __rhs)
{ __ios_type::swap(__rhs); }
#endif
template<typename _ValueT>
__ostream_type&
_M_insert(_ValueT __v);
};
/**
* @brief Performs setup work for output streams.
*
* Objects of this class are created before all of the standard
* inserters are run. It is responsible for <em>exception-safe prefix and
* suffix operations</em>.
*/
template <typename _CharT, typename _Traits>
class basic_ostream<_CharT, _Traits>::sentry
{
// Data Members.
bool _M_ok;
basic_ostream<_CharT, _Traits>& _M_os;
public:
/**
* @brief The constructor performs preparatory work.
* @param __os The output stream to guard.
*
* If the stream state is good (@a __os.good() is true), then if the
* stream is tied to another output stream, @c is.tie()->flush()
* is called to synchronize the output sequences.
*
* If the stream state is still good, then the sentry state becomes
* true (@a okay).
*/
explicit
sentry(basic_ostream<_CharT, _Traits>& __os);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
/**
* @brief Possibly flushes the stream.
*
* If @c ios_base::unitbuf is set in @c os.flags(), and
* @c std::uncaught_exception() is true, the sentry destructor calls
* @c flush() on the output stream.
*/
~sentry()
{
// XXX MT
if (bool(_M_os.flags() & ios_base::unitbuf) && !uncaught_exception())
{
// Can't call flush directly or else will get into recursive lock.
if (_M_os.rdbuf() && _M_os.rdbuf()->pubsync() == -1)
_M_os.setstate(ios_base::badbit);
}
}
#pragma GCC diagnostic pop
/**
* @brief Quick status checking.
* @return The sentry state.
*
* For ease of use, sentries may be converted to booleans. The
* return value is that of the sentry state (true == okay).
*/
#if __cplusplus >= 201103L
explicit
#endif
operator bool() const
{ return _M_ok; }
};
//@{
/**
* @brief Character inserters
* @param __out An output stream.
* @param __c A character.
* @return out
*
* Behaves like one of the formatted arithmetic inserters described in
* std::basic_ostream. After constructing a sentry object with good
* status, this function inserts a single character and any required
* padding (as determined by [22.2.2.2.2]). @c __out.width(0) is then
* called.
*
* If @p __c is of type @c char and the character type of the stream is not
* @c char, the character is widened before insertion.
*/
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
{ return __ostream_insert(__out, &__c, 1); }
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
{ return (__out << __out.widen(__c)); }
// Specialization
template <class _Traits>
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, char __c)
{ return __ostream_insert(__out, &__c, 1); }
// Signed and unsigned
template<class _Traits>
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
{ return (__out << static_cast<char>(__c)); }
template<class _Traits>
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
{ return (__out << static_cast<char>(__c)); }
//@}
//@{
/**
* @brief String inserters
* @param __out An output stream.
* @param __s A character string.
* @return out
* @pre @p __s must be a non-NULL pointer
*
* Behaves like one of the formatted arithmetic inserters described in
* std::basic_ostream. After constructing a sentry object with good
* status, this function inserts @c traits::length(__s) characters starting
* at @p __s, widened if necessary, followed by any required padding (as
* determined by [22.2.2.2.2]). @c __out.width(0) is then called.
*/
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
{
if (!__s)
__out.setstate(ios_base::badbit);
else
__ostream_insert(__out, __s,
static_cast<streamsize>(_Traits::length(__s)));
return __out;
}
template<typename _CharT, typename _Traits>
basic_ostream<_CharT, _Traits> &
operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s);
// Partial specializations
template<class _Traits>
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
{
if (!__s)
__out.setstate(ios_base::badbit);
else
__ostream_insert(__out, __s,
static_cast<streamsize>(_Traits::length(__s)));
return __out;
}
// Signed and unsigned
template<class _Traits>
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)
{ return (__out << reinterpret_cast<const char*>(__s)); }
template<class _Traits>
inline basic_ostream<char, _Traits> &
operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)
{ return (__out << reinterpret_cast<const char*>(__s)); }
//@}
// Standard basic_ostream manipulators
/**
* @brief Write a newline and flush the stream.
*
* This manipulator is often mistakenly used when a simple newline is
* desired, leading to poor buffering performance. See
* https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html#io.streambuf.buffering
* for more on this subject.
*/
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
endl(basic_ostream<_CharT, _Traits>& __os)
{ return flush(__os.put(__os.widen('\n'))); }
/**
* @brief Write a null character into the output sequence.
*
* <em>Null character</em> is @c CharT() by definition. For CharT
* of @c char, this correctly writes the ASCII @c NUL character
* string terminator.
*/
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
ends(basic_ostream<_CharT, _Traits>& __os)
{ return __os.put(_CharT()); }
/**
* @brief Flushes the output stream.
*
* This manipulator simply calls the stream's @c flush() member function.
*/
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
flush(basic_ostream<_CharT, _Traits>& __os)
{ return __os.flush(); }
#if __cplusplus >= 201103L
template<typename _Ch, typename _Up>
basic_ostream<_Ch, _Up>&
__is_convertible_to_basic_ostream_test(basic_ostream<_Ch, _Up>*);
template<typename _Tp, typename = void>
struct __is_convertible_to_basic_ostream_impl
{
using __ostream_type = void;
};
template<typename _Tp>
using __do_is_convertible_to_basic_ostream_impl =
decltype(__is_convertible_to_basic_ostream_test
(declval<typename remove_reference<_Tp>::type*>()));
template<typename _Tp>
struct __is_convertible_to_basic_ostream_impl
<_Tp,
__void_t<__do_is_convertible_to_basic_ostream_impl<_Tp>>>
{
using __ostream_type =
__do_is_convertible_to_basic_ostream_impl<_Tp>;
};
template<typename _Tp>
struct __is_convertible_to_basic_ostream
: __is_convertible_to_basic_ostream_impl<_Tp>
{
public:
using type = __not_<is_void<
typename __is_convertible_to_basic_ostream_impl<_Tp>::__ostream_type>>;
constexpr static bool value = type::value;
};
template<typename _Ostream, typename _Tp, typename = void>
struct __is_insertable : false_type {};
template<typename _Ostream, typename _Tp>
struct __is_insertable<_Ostream, _Tp,
__void_t<decltype(declval<_Ostream&>()
<< declval<const _Tp&>())>>
: true_type {};
template<typename _Ostream>
using __rvalue_ostream_type =
typename __is_convertible_to_basic_ostream<
_Ostream>::__ostream_type;
/**
* @brief Generic inserter for rvalue stream
* @param __os An input stream.
* @param __x A reference to the object being inserted.
* @return os
*
* This is just a forwarding function to allow insertion to
* rvalue streams since they won't bind to the inserter functions
* that take an lvalue reference.
*/
template<typename _Ostream, typename _Tp>
inline
typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>,
__is_convertible_to_basic_ostream<_Ostream>,
__is_insertable<
__rvalue_ostream_type<_Ostream>,
const _Tp&>>::value,
__rvalue_ostream_type<_Ostream>>::type
operator<<(_Ostream&& __os, const _Tp& __x)
{
__rvalue_ostream_type<_Ostream> __ret_os = __os;
__ret_os << __x;
return __ret_os;
}
#endif // C++11
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#include <bits/ostream.tcc>
#endif /* _GLIBCXX_OSTREAM */
istream
库
istream
是 C++ 标准库中的窄字节输入库,主要用于处理输入操作。由于代码较长且未在原文中提供,此处略去详细代码展示。
fstream
库
fstream
库是 C++ 标准库中用于文件操作的头文件,提供了文件输入输出流的功能,支持对文件的读写操作。
ios
库
ios
库是 C++ 标准库中用于管理输入输出流的基础库,类似于 Java 中的 iostream
库。它为输入输出操作提供了底层支持,并定义了流的状态标志、格式化标志等。需要注意的是,ios
并非用于 iOS 开发的库,而是 C++ 标准库的一部分。
本文完。
C++ 流(stream)总结
浩世轩宇 于 2014-11-10 17:22:27 发布
一、C++ 中流的概念
在程序设计中,数据输入/输出(I/O)操作是必不可少的,C++语言的数据输入/输出操作是通过 I/O 流库来实现的。C++ 中把数据之间的传输操作称为流,流既可以表示数据从内存传送到某个载体或设备中(即输出流),也可以表示数据从某个载体或设备传送到内存缓冲区变量中(即输入流)。
C++ 流涉及以下概念:
- 标准 I/O 流:内存与标准输入输出设备之间信息的传递;
- 文件 I/O 流:内存与外部文件之间信息的传递;
- 字符串 I/O 流:内存变量与表示字符串流的字符数组之间信息的传递。
STL 中定义的流类
流类分类 | 流类名称 | 流类作用 |
---|---|---|
流基类 | ios | 所有流类的父类,保存流的状态并处理错误 |
输入流类 | istream | 输入流基类,将流缓冲区中的数据作格式化和非格式化之间的转换并输入 |
ifstream | 文件输入流类 | |
istream_withassign | cin 输入流类,即操作符 >> 输入流 | |
istrstream | 串输入流类,基于 C 类型字符串 char* 编写 | |
istringstream | 串输入流类,基于 std::string 编写 | |
输出流类 | ostream | 输出流基类,将流缓冲区中的数据作格式化和非格式化之间的转换并输出 |
ofstream | 文件输出流类 | |
ostream_withassign | cout 、cerr 、clog 的输出流类,即操作符 << 输出流 | |
ostrstream | 串输入流类,基于 C 类型字符串 char* 编写 | |
ostringstream | 串输入流类,基于 std::string 编写 | |
输入/输出流类 | iostream | 多目的输入/输出流类的基类 |
fstream | 文件流输入/输出类 | |
strstream | 串流输入/输出类,基于 C 类型字符串 char* 编写 | |
stringstream | 串流输入/输出类,基于 std::string 编写 |
注:对于串流,提供了两套类,一个基于 C 类型字符串 char*
编写(定义于头文件 <strstream>
),一个基于 std::string
编写(定义于 <sstream>
),后者是 C++ 标准委员会推荐使用的。
二、C++ 中读取 string 对象
-
标准输入读取:
cin >> string
- 忽略开头所有的空白字符(空格、换行、制表符等);
- 读取字符直至再次遇到空白字符,读取终止。
-
读取整行文本:
getline(istream, string)
- 不忽略开头的空白字符;
- 读取字符直至遇到换行符,如果第一个字符是换行符,则返回空
string
; - 返回时丢弃换行符,换行符不存储在
string
中。
三、对 sstream(经常用作格式转换)库的讲解
使用 stringstream 对象简化类型转换
如果你已习惯了 <stdio.h>
风格的转换,也许你首先会问:为什么要花额外的精力来学习基于 <sstream>
的类型转换呢?也许对下面一个简单的例子的回顾能够说服你。假设你想用 sprintf()
函数将一个变量从 int
类型转换到字符串类型。为了正确地完成这个任务,你必须确保证目标缓冲区有足够大空间以容纳转换完的字符串。此外,还必须使用正确的格式化符。如果使用了不正确的格式化符,会导致非预知的后果。下面是一个例子:
int n = 10000;
char s[10];
sprintf(s, "%d", n); // s 中的内容为 "10000"
到目前为止看起来还不错。但是,对上面代码的一个微小的改变就会使程序崩溃:
int n = 10000;
char s[10];
sprintf(s, "%f", n); // 看!错误的格式化符
在这种情况下,程序员错误地使用了 %f
格式化符来替代了 %d
。因此,s
在调用完 sprintf()
后包含了一个不确定的字符串。要是能自动推导出正确的类型,那不是更好吗?
进入 stringstream
由于 n
和 s
的类型在编译期就确定了,所以编译器拥有足够的信息来判断需要哪些转换。<sstream>
库中声明的标准类就利用了这一点,自动选择所必需的转换。而且,转换结果保存在 stringstream
对象的内部缓冲中。你不必担心缓冲区溢出,因为这些对象会根据需要自动分配存储空间。
注意:<sstream>
使用 string
对象来代替字符数组。这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即使使用了不正确的格式化符也没有危险。
string 到 int 的转换
string result = "10000";
int n = 0;
stringstream stream;
stream << result;
stream >> n; // n 等于 10000
重复利用 stringstream 对象
如果你打算在多次转换中使用同一个 stringstream
对象,记住在每次转换前要使用 clear()
方法。在多次转换中重复使用同一个 stringstream
(而不是每次都创建一个新的对象)对象最大的好处在于效率。stringstream
对象的构造和析构函数通常是非常耗费 CPU 时间的。
在类型转换中使用模板
你可以轻松地定义函数模板来将一个任意的类型转换到特定的目标类型。例如,需要将各种数字值,如 int
、long
、double
等等转换成字符串,要使用以一个 string
类型和一个任意值 t
为参数的 to_string()
函数。to_string()
函数将 t
转换为字符串并写入 result
中。使用 str()
成员函数来获取流内部缓冲的一份拷贝:
template <class T>
void to_string(string &result, const T &t) {
ostringstream oss; // 创建一个流
oss << t; // 把值传递入流中
result = oss.str(); // 获取转换后的字符串并将其写入 result
}
这样,你就可以轻松地将多种数值转换成字符串了:
to_string(s1, 10.5); // double 到 string
to_string(s2, 123); // int 到 string
to_string(s3, true); // bool 到 string
可以更进一步定义一个通用的转换模板,用于任意类型之间的转换。函数模板 convert()
含有两个模板参数 out_type
和 in_value
,功能是将 in_value
值转换成 out_type
类型:
template <class out_type, class in_value>
out_type convert(const in_value &t) {
stringstream stream;
stream << t; // 向流中传值
out_type result; // 这里存储转换结果
stream >> result; // 向 result 中写入值
return result;
}
这样使用 convert()
:
double d;
string salary;
string s = "12.56";
d = convert<double>(s); // d 等于 12.56
salary = convert<string>(9000.0); // salary 等于 "9000"
stringstream
通常是用来做数据转换的。相比 C 库的转换,它更加安全、自动和直接。
四、STL 中 string
、ifstream
、stringstream
的使用
1. 从标准输入接受字符串,然后进行相关处理
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
string s; // 定义一个 string 对象,从标准输入接受一个字符串
cout << "请输入一行字符串:" << endl;
getline(cin, s);
stringstream ss(s); // 定义一个 string 流(使用 s 实例化)
cout << "处理后的字符串为:" << endl; // 将 string 流里的东西输出
for (string s1; ss >> s1; cout << s1 << endl);
return 0;
}
运行结果如下:
请输入一行字符串:
you are a good boy
处理后的字符串为:
you
are
a
good
boy
根据前文所说“忽略开头空白字符,读取字符直至再次遇到空白字符为止”,这样的结果不难理解。
2、getline 函数原型
template <class E, class T, class A> basic_istream<E, T>& getline(
basic_istream<E, T>& is, basic_string<E, T, A>& str);
template <class E, class T, class A> basic_istream<E, T>& getline(
basic_istream<E, T>& is, basic_string<E, T, A>& str, E delim);
第二个重载函数很有意思,结尾是 char
或 wchar
型的一个分隔符。如果设为 '\n'
,则为以换行符为分隔;如果设为 ','
,则为以逗号为分隔。由此,虽然 C++ 中的字符串没有分割函数,如果是从文件中读取出被特定分隔符分隔的文本,那么就可以用此方法,如:
std::ifstream file;
file.open("tt.txt");
std::string s, t;
while (std::getline(file, s)) // 按行读取
{
std::stringstream strs(s); // 把行装到另一个流中
while (std::getline(strs, t, ',')) // 把流按分隔符实现分割
std::cout << t << std::endl;
}
file.close();
上面的程序相当于将整个文本先按行分割,再按分隔符分割,也可以变换一下,只按分隔符分割,然后过滤掉按行符,换行符与某元素连在了一起,并处于开头。
五、C++ 的流操作复制文件
写 .wrl
、.obj
文件格式转换时用到,记录一下相关方法。
使用 C++ 标准程序库的输入输出流(I/O Stream)复制文件,存在许多的方法。
方法一:逐个字符复制
#include <fstream>
std::ifstream input("in", ios::binary);
std::ofstream output("out", ios::binary);
char ch;
while (input.get(ch)) output << ch;
注意:如果使用 input >> ch
读取字符,则必须先调用 input.unsetf(ios::skipws)
取消输入流默认的跳过空白符(空格、换行、制表符等)的输入格式,因为换行符是空白符的一种。另外,对于 ofstream
对象,默认的操作方式是 ios_base::out | ios_base::trunc
,即输出和文件清空!
方法二:逐行复制
#include <fstream>
#include <string>
std::ifstream input("in", ios::binary);
std::ofstream output("out", ios::binary);
std::string line;
while (getline(input, line)) output << line << "\n";
注意:这里的代码有一个小小的缺陷,如果文件不是纯文本格式的文件,或者文本文件的最后没有换行符,那么会导致复制后的文件末尾添加了一个多余的换行符。
方法三:迭代器复制
#include <fstream>
#include <iterator>
#include <algorithm>
std::ifstream input("in", ios::binary);
std::ofstream output("out", ios::binary);
input.unsetf(ios::skipws);
copy(istream_iterator<char>(input), istream_iterator<char>(), ostream_iterator<char>(output, ""));
同样这里也有一个小技巧,输入流的格式默认为跳过空白字符,因此调用 unsetf
取消这个格式,才可保证正确的复制。
方法四:缓冲区复制
#include <fstream>
std::ifstream input("in", ios::binary);
std::ofstream output("out", ios::binary);
output << input.rdbuf();
这里直接使用了输入流的缓冲区,因此没有引入额外的临时对象。
很显然,上述四种方法中,最后一种方法最简洁,由于直接操作输入流的缓冲区,从运行效率上来说,也比其他方法有着略微的优势(当然,由于操作系统可能提供了额外的基于设备的文件缓冲机制,也许你无法证实这一点)。因此,除非要对输入内容进行处理,直接复制文件推荐最后一种方法,既不容易出错,又能获得良好的性能。
另外,对文件进行更改、删除、插入等操作,可以直接用以上方法也可以先把文件读入 vector<string>
,处理后再输出,不过当文件很大的时候,vector
占用内存空间较大,而且输出时会破坏原文件格式,尽量不使用。
以上 是几种同种流(文件流)之间的数据复制的方式,字符串流与文件流之间也可以以此方式进行复制。另外,再看一下 get
函数:
int_type get();
basic_istream& get(E& c);
basic_istream& get(E *s, streamsize n);
basic_istream& get(E *s, streamsize n, E delim);
basic_istream& get(basic_streambuf<E, T> *sb);
basic_istream& get(basic_streambuf<E, T> *sb, E delim);
delim
表示结束符,前文中讨论的流分割函数还记得吧?又多了种方法,估计 get
与全局函数 getline
(不是那个成员函数,成员函数要求给出 streamsize
,而全局的就不用)实现得差不多。默认的也是 '\n'
,按行分隔。
六、C++ 流缓冲区的应用——输出文件内容的方法举例
简单讨论 C++ 流对象的底层缓冲区,并举例介绍如何使用该缓冲区进行文件内容的输出。
C++ 标准库封装了一个缓冲区类 streambuf
,以供输入输出流对象使用。每个标准 C++ 输出输出流对象都包含一个指向 streambuf
的指针,用户可以通过调用 rdbuf()
成员函数获得该指针,从而直接访问底层 streambuf
对象。因此,可以直接对底层缓冲区进行数据读写,从而跳过上层的格式化输入输出操作。
template <class Elem, class Traits> class basic_ios : public ios_base {
basic_streambuf<_Elem, _Traits>* _Mystrbuf;
// C++ 标准库封装了一个缓冲区类 streambuf。
_Mysb* rdbuf() const { // return stream buffer pointer
return (_Mystrbuf);
}
// 使调用者与参数(流缓冲指针)关联,
// 返回自己当前关联的流缓冲区指针,可用来重定向等
_Mysb* rdbuf(_Mysb* _Strbuf) { // set stream buffer pointer
_Mysb* _Oldstrbuf = _Mystrbuf;
_Mystrbuf = _Strbuf;
return (_Oldstrbuf);
}
};
对象通过调用 rdbuf()
获得了底层 streambuf
对象的指针,也就可以通过该指针调用 streambuf
支持你各种操作进行输入输出。在这里主要介绍如何利用该指针实现文件内容的输出。
对于文件流类和字符串流类,分别派生了相应的流缓冲区类型:
template <class _Elem, class _Traits> class basic_streambuf;
typedef basic_streambuf<char, char_traits<char>> streambuf;
typedef basic_streambuf<wchar_t, char_traits<wchar_t>> wstreambuf;
template <class Elem, class Tr = char_traits<Elem>, class Alloc = allocator<Elem>>
class basic_stringbuf : public basic_streambuf<Elem, Tr>;
typedef basic_stringbuf<char, char_traits<char>, allocator<char>> stringbuf;
typedef basic_stringbuf<wchar_t, char_traits<wchar_t>, allocator<wchar_t>> wstringbuf;
template <class Elem, class Tr = char_traits<Elem>> class basic_filebuf : public basic_streambuf<Elem, Tr>;
typedef basic_filebuf<char, char_traits<char>> filebuf;
typedef basic_filebuf<wchar_t, char_traits<wchar_t>> wfilebuf;
输出流提供了一个重载版本 operator<<
,它以 streambuf
指针为参数,实现把 streambuf
对象中的所有字符输出到输出流出中;输入流也提供了一个对应的 operator>>
重载版本,把输入流对象中的所有字符输入到 streambuf
对象中。输入流的 get
成员重载版本中还有以 streambuf
指针为参数的版本,也可以用来把输入流的东西写入到输出流缓冲区中。
下面用三种方法实现把一个文件的内容输出标准输出(当然还可以通过普通的标准格式化输入输出完成):
方法一:通过 operator<<
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream fin("source.dat");
cout << fin.rdbuf();
return 0;
}
方法二:利用 get
成员函数
ifstream fin("source.dat");
while (!fin.get(*cout.rdbuf()).eof()) { // extract a line input
if (fin.fail()) // blank line
fin.clear();
cout << char(fin.get()); // extract '\n'
}
代码解释:由于上面代码中的 get
版本在遇到 '\n'
字符时,结束提取,所以需要用循环实现整个文件内容的输出。另外,当此版本 get
函数遇到空行时,因为没有提取到任何字符(注意:get
不提取回车符),注意会设置失败标志 ios::failbit
,所以此时应当调用 clear()
函数清除错误标志。同样,因为该版本 get
不会提取回车符,所以需要用另一版本的 get()
提取回车符。不同版本的 Get
函数参见前文。此处还使用了 *cout.rdbuf()
,cout
是 ostream
类的对象,当然就有缓冲区,可以用 rdbuf
返回缓冲区对象的指针。最后,关于 fin.clear
,需要特别注意的是:要清空流类对象的内存,不能用 clear
方法,那只是设置了错误标志位。
方法三:利用重载的 get
函数
ifstream fin("main.cpp");
fin.get(*cout.rdbuf(), EOF);
代码解释:这个版本的 get
成员函数可以自定义提取终止符。这里通过设置为文件结束符(EOF
)来达到一下提取整个文件的目的。
当然,你可以把上面的 cout
换成任意的输出流,比如文件输出流,从而可以实现文件的拷贝功能。
另外,上面代码中并没有使用输入流的 >>
操作符,因为 >>
和 <<
是相对的,只是把操作数交换一下位置罢了。因此,你可以把上面代码中用 out << in.rdbuf()
的地方换成 in >> out.rdbuf()
,代码的功能完全一样,效果也一样。
七、关于缓冲区
1. 缓冲类型
标准库提供缓冲是为了减少对 read
和 write
的调用。提供的缓冲有三种类型(整理自 APUE):
-
全缓冲:在这种情况下,实际的 I/O 操作只有在缓冲区被填满了之后才会进行。对驻留在磁盘上的文件的操作一般是有标准 I/O 库提供全缓冲。缓冲区一般是在第一次对流进行 I/O 操作时,由标准 I/O 函数调用
malloc
函数分配得到的。flush
描述了标准 I/O 缓冲的写操作。缓冲区可以由标准 I/O 函数自动flush
(例如缓冲区满的时候);或者我们对流调用fflush
函数。 -
行缓冲:在这种情况下,只有在输入/输出中遇到换行符的时候,才会执行实际的 I/O 操作。这允许我们一次写一个字符,但是只有在写完一行之后才做 I/O 操作。一般的,涉及到终端的流——例如标准输入(
stdin
)和标准输出(stdout
)——是行缓冲的。 -
无缓冲:标准 I/O 库不缓存字符。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。
ISO C 要求:
- 当且仅当不涉及交互设备时,标准输入和标准输出是全缓存的。
- 标准错误绝对不是全缓存的。
但是,这并没有告诉我们当标准输入/输出在涉及交互设备时,它们是无缓存的还是行缓存的;也没有告诉我们标准错误应该是行缓存的还是无缓存的。不过,大多数实现默认的缓存类型是这样的:
- 标准错误总是无缓存的。
- 对于所有的其他流来说,如果它们涉及到交互设备,那么就是行缓存的;否则是全缓存的。
2. 改变默认缓存类型,即自定义缓冲区
a. 对于 C 中文件操作
可以通过下面的函数改变缓存类型(摘自 APUE):
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
这些函数必须在流打开之后、但是未对流做任何操作之前被调用(因为每个函数都需要一个有效的文件指针作为第一个参数)。
利用 setbuf
,可以打开或者关闭缓存。为了打开缓存,buf
参数必须一个大小为 BUFSIZ
的缓存,BUFSIZ
是定义在 stdio.h
中的常量。<ISO/IEC 9899>
要求:BUFSIZ
至少为 256。如果要关闭缓存,可以将 buf
设成 NULL
。
利用 setvbuf
,我们可以设定缓存类型。这是通过 mode
参数指定的。
b. 对于 C++ 中流
virtual basic_streambuf* setbuf(E *s, streamsize n);
第一个参数指向自定义缓冲区空间,第二个为缓冲区大小。
八、string
和 stringBuffer
的区别
String
对象建立之后不能再改变,如果经常对字符串进行各种各样的修改,那么使用 String
来代表字符串的话会引起很大的内存开销。StringBuffer
允许修改,不是每个不同的字符串都要生成一个新的对象,两种类的对象转换十分容易。
在我以前的了解中,String
是一个 final Class
,StringBuffer
不是。所以对于 String a = "yacht"
,String b = "yacht1"
,String c = a + b
;存在一个对象拷贝构造和解析的消耗问题;对于一个 StringBuffer
来说,StringBuffer sb = new StringBuffer()
;sb.append("yacht")
;sb.append("yacht1")
;因为 StringBuffer
是一个可以实例化的类,而且它的内建机制是维护了一个 capacity
大小的字符数组,所以它的 append
操作不存在对象的消耗问题,所以我觉得如果存在 String
连接这种事情,StringBuffer
来做会好很多。
但事情并不是这么简单,看下面代码:
String a = "yacht1" + "yacht2" + "yacht3" + "yacht4";
StringBuffer sb = new StringBuffer();
sb.append("yacht1");
sb.append("yacht2");
sb.append("yacht3");
sb.append("yacht4");
String a = sb.toString();
如果按照我先前说的看法,红色的效率肯定比蓝色的低,但经过测试不是这样,为什么?这里,我们需要理解程序过程的两个时期,一个是编译时,一个是运行时,在编译时,编译器会对你的程序做出优化,所以红色的 String a
会被优化成 yacht1yacht2yacht3yacht4
,而蓝色的 StringBuffer
只会在运行时才处理。所以效率是不一样的。
如果代码是这样的:
String a;
for (int i = 0; i < 100000; i++) {
a += String.valueOf(i);
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 100000; i++) {
sb.append(i);
}
String a = sb.toString();
如果是这种情况的话,红色的效率就大大不如蓝色,区别在哪里,就在于运行时和编译时的优化问题上!
前面看到有人写 String
和 stringBuffer
的区别是前者是不能改写的,后者是可以改写的。
我觉得说 String
的字符串不能改变话是不错,但是例子要举好。
看看下面这个简单的例子:
public class xx {
public static void main(String[] args) {
String s1 = "You are hired!";
String s2 = "You are hired!";
if (s1 == s2) {
System.out.println("一个内存空间");
} else {
System.out.println("不是一个内存空间");
}
}
}
打印的结果是:一个内存空间
这里 ==
的意义是两个操作数是否指向同一个对象。
可见 s2
在不用 new
创建的情况下会自动检索到具有相同内容的内存空间中共享,那么既然 s1
和 s2
共享了同一个对象。
再看下面的代码:
public class xx {
public static void main(String[] args) {
String s1 = "You are hired!";
String s2 = "You are hired!";
s1 = s1.replace('h', 'f');
System.out.println(s1);
if (s1 == s2) {
System.out.println("一个内存空间");
} else {
System.out.println("不是一个内存空间");
}
}
}
代码结果是:
You are fired!
不是一个内存空间
可见,String
中 s1
的内容虽然被改写,但是已经不在是原来第一次分配到的那个内存空间,也就是 String
类的内容能被改变,但一旦改变系统将为其分配新的内存。
说到与 stringBuffer
的区别,从根本上来说应该是:
stringBuffer
在做字符长度变动的时候将继续使用原来的内存空间,不新分配。
而 String
的长度一旦变动,就如上面的例子一样,其内部将分配新的内存空间。
九、streambuf
相关的讲解
顺序访问流中的字符。如果用一个数组模拟出流,代码将是下面样子的。
char sgetc() { return buff[i]; }
char sbumpc() { return buff[i++]; } // 先取当前字符,然后指针移向后一个
char snextc() { return buff[++i]; } // 先移动指针,然后取值
逐个字符的打印出 abcdefg
:
stringstream ss;
ss << "abcdefg";
streambuf* pbuf = ss.rdbuf();
do {
char ch = pbuf->sgetc();
cout << ch;
} while (pbuf->snextc() != EOF);
验证缓冲区在起作用,pbuf->in_avail()
返回的值在变化:
fstream fs;
fs.open("C:\\test.log");
streambuf* pbuf = fs.rdbuf();
while (pbuf->sgetc() != EOF) {
char ch = pbuf->sbumpc(); // 弹出一个字符
cout << ch;
streamsize size = pbuf->in_avail(); // 查询缓冲区内有效数据个数(把缓存区看做一个容器)
}
实现文件大小的查询(C 函数 fseek
,ftell
):
fstream filestr("C:\\test.log");
streambuf* pbuf = filestr.rdbuf();
size = pbuf->pubseekoff(0, ios_base::end);
设置程序员选择的任意缓冲区(C 函数 setvbuf
):
fstream ss("C:\\test.log");
streambuf* pbuf = ss.rdbuf();
char mybuffer[2048] = {0};
pbuf->pubsetbuf(mybuffer, 2048);
读取出来的一个字符,可以做退回操作:
ch = pbuf->sbumpc();
int r = pbuf->sputbackc(ch);
ch = pbuf->sgetc();
十、C++ 中控制文件读写位置
C++ 输出控制
如果要在输出流中加入格式控制符则要加载头文件:#include <iomanip>
。这里面 iomanip
的作用比较多,主要是对 cin
、cout
之类的一些操纵运算子,比如 setfill
、setw
、setbase
、setprecision
等等。它是 I/O 流控制头文件,就像 C 里面的格式化输出一样。以下是一些常见的控制函数:
dec
:置基数为 10,相当于"%d"
。hex
:置基数为 16,相当于"%X"
。oct
:置基数为 8,相当于"%o"
。
示例:
cout << 12 << hex << 12 << oct << 12 << 12; // output 12c1414
setprecision(n)
:设显示小数精度为n
位。
示例:
setf(ios::fixed);
cout << setprecision(2) << 2.345 << endl; // output 2.34
setw(n)
:设域宽为n
个字符。这个控制符的意思是保证输出宽度为n
。如:
cout << setw(3) << 1 << setw(3) << 10 << setw(3) << 100; // 输出结果为 1 10100
当输出长度大于 3
时(<<1000
),setw(3)
不起作用。
-
setfill(c)
:设填充字符为c
。 -
setiosflags(ios::fixed)
:固定的浮点显示。 -
setiosflags(ios::scientific)
:指数表示。
示例:
cout << setiosflags(ios::fixed) << setprecision(2) << 2.345 << endl; // output 2.34
setiosflags(ios::left)
:左对齐。setiosflags(ios::right)
:右对齐。setiosflags(ios::skipws)
:忽略前导空白。setiosflags(ios::uppercase)
:16 进制数大写输出。setiosflags(ios::lowercase)
:16 进制小写输出。setiosflags(ios::showpoint)
:强制显示小数点。setiosflags(ios::showpos)
:强制显示符号。
示例:
cout << setiosflags(ios::uppercase) << hex << 12 << 15 << endl; // output CF
cout << setiosflags(ios::showpoint) << x << endl; // 若 float x = 1, 则 output 1.000000
cout << setiosflags(ios::showpos) << 1 << endl; // output +1
其他流函数
bits
:指定的格式标志位,返回旧的格式标志。long fill(char c)
:设置填充字符,缺省条件下是空格。char fill()
:返回当前填充字符。int precision(int val)
:设置精确度为val
,控制输出浮点数的有效位,返回旧值。int precision()
:返回旧的精确度值。int width(int val)
:设置显示数据的宽度(域宽),返回旧的域宽。int width()
:只返回当前域宽,缺省宽度为0
。这时插入操作能按表示数据的最小宽度显示数据。
预定义的操纵算子
使用成员函数控制格式化输入输出时,每个函数调用需要写一条语句,尤其是它不能用在插入或提取运算符的表达式中,而使用操纵算子,则可以在插入和提取运算符的表达式中控制格式化输入和输出。在程序中使用操纵算子必须嵌入头文件 <iomanip.h>
。
dec
:十进制的输入输出。hex
:十六进制的输入输出。oct
:八进制的输入输出。ws
:提取空白字符。ends
:输出一个nul
字符。endl
:输出一个换行字符,同时刷新流。flush
:刷新流。resetiosflags(long)
:清除特定的格式标志位。setiosflags(long)
:设置特定的格式标志位。setfill(char)
:设置填充字符。setprecision(int)
:设置输出浮点数的精确度。setw(int)
:设置域宽格式变量。
错误处理
在对一个流对象进行 I/O 操作时,可能会产生错误。当错误发生时,错误的性质被记录在 ios
类的一个数据成员中。ios
类中定义的描述错误状态的常量:
goodbit
:没有错误,正常状态。eofbit
:到达流的结尾。failbit
:I/O 操作失败,清除状态字后,可以对流继续进行操作。badbit
:试图进行非法操作,清除状态字后,流可能还可以使用。hardfail
:致命错误,不可恢复的错误。
ostream
类的成员函数
ostream& put(char ch)
:向流中输出一个字符ch
,不进行任何转换。ostream& write(char*, int)
:向流中输出指定长度的字符串,不进行转换。ostream& flush()
:刷新流,输出所有缓冲的但还未输出的数据。ostream& seekp(streampos)
:移动流的当前指针到给定的绝对位置。ostream& seekp(streamoff, seek_dir)
:流的当前指针类似与文件的当前指针。streampos tellp()
:返回流的当前指针的绝对位置。
istream
类的成员函数
int get()
:读取并返回一个字符。istream& get(char& c)
:读取字符并存入c
中。istream& get(char* ptr, int len, char delim = '')
:读取指定的字符到缓冲区中,直到遇到指定的分界符为止,分界符不填入缓冲区。istream& getline(char* ptr, int len, char delim = '')
:与get(char*, int, char)
类似,但将分界符填入缓冲区。istream& putback()
:将最近读取的字符放回流中。istream& read(char*, int)
:读取规定长度的字符串到缓冲区中。int gcount()
:返回读取的字节数。int peek()
:返回流中下一个字符,但不移动文件指针。istream& seekg(streampos)
:移动当前指针到一绝对地址。istream& seekg(streampos, seek_dir)
:移动当前指针到一相对地址。streampos tellg()
:返回当前指针。istream& ignore(int n = 1, delim = EOF)
:跳过流中几个字符,或直到遇到指定的分界符为止。
其他函数
getch()
:函数用于从键盘输入一个字符,不回显,包含头文件<conio.h>
中。
via :
-
Understanding the Utility of Iostreams in C++ | CodeGuru
https://www.codeguru.com/cplusplus/understanding-the-utility-of-iostreams-in-c/ -
C++ I/O
https://www.cs.fsu.edu/~lacher/lectures/Output/io/script.html -
Cpp_Streams.pdf
https://web.stanford.edu/class/archive/cs/cs106b/cs106b.1084/cs106l/handouts/030_Cpp_Streams.pdf -
C++的 iostream 标准库介绍+使用详解(转) - 极客先锋 - 博客园
https://www.cnblogs.com/jikexianfeng/articles/5651661.html -
C++ 中头文件 iostream 介绍 - CSDN 博客
https://blog.csdn.net/fengbingchun/article/details/63685373- GitHub - fengbingchun/Messy_Test: C++/C++11/C++14/C++17/C++20’s usage
https://github.com/fengbingchun/Messy_Test
- GitHub - fengbingchun/Messy_Test: C++/C++11/C++14/C++17/C++20’s usage
-
C++ 里面的 iostream 是什么_c++ iostream-CSDN 博客
https://blog.csdn.net/weixin_43469680/article/details/88863207 -
C++——IOStream_c++ iostream-CSDN 博客
https://blog.csdn.net/qq_74260823/article/details/135768853 -
C++ iostream、ostream、istream 等标准库都是什么?看完这篇就知道了-CSDN博客
https://blog.csdn.net/weixin_59197425/article/details/126567402 -
C++ 流(stream)总结_c++ stream-CSDN博客
https://blog.csdn.net/luguifang2011/article/details/40979231