注:本文為 “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 methodsbool good() const; // true iff no error flag is setbool eof() const; // true iff stream is at end of filebool fail() const; // true iff badbit or failbit are setbool bad() const; // true if badbit is setoperator void*() const; // null pointer if fail(), non-null otherwisevoid clear(iostate newstate = goodbit); // sets state to newstatevoid setstate(iostate addstate); // adds addstate to existing stateenum iostate {goodbit = 0x0000, // everything's okeofbit = 0x0001, // stream is at end of filefailbit = 0x0002, // last I/O operation failedbadbit = 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 setbool eof() const; // true if eofbit is setbool fail() const; // true if badbit or failbit are setbool bad() const; // true if badbit is setoperator void*() const; // null pointer if fail(), non-null otherwisevoid clear(iostate newstate = goodbit); // sets state to newstatevoid setstate(iostate addstate); // adds addstate to existing stateenum iostate {goodbit = 0x0000, // everything's okeofbit = 0x0001, // stream is at end of filefailbit = 0x0002, // last I/O operation failedbadbit = 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 flagsfmtflags flags(fmtflags newflags); // sets flags to newflagsfmtflags setf(fmtflags setbits); // sets specified flagsfmtflags setf(fmtflags setbits, fmtflags mask); // sets flags in maskfmtflags unsetf(fmtflags unsetbits); // clears specified flagsenum fmtflags {boolalpha = 0x0001, // read/write bool values in alphabeticleft = 0x0002, // left-justify outputright = 0x0004, // right-justify outputinternal = 0x0008, // prefix left..fill..number rightskipws = 0x0010, // skip white space before extractiondec = 0x0020, // decimalhex = 0x0040, // hexadecimaloct = 0x0080, // octalshowbase = 0x0100, // show base indicator on outputshowpoint = 0x0200, // show decimal point for fixed point outputshowpos = 0x0400, // force show of sign for positive numbersfixed = 0x0800, // force decimal notation for floatscientific = 0x1000, // force scientific notation for floatunitbuf = 0x2000, // flush buffer after each insertionuppercase = 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 characterint precision() const; // returns precision valueint precision(int val); // sets precision valueint width() const; // returns width valueint width(int val); // sets width valueprotected:int width_value; // initialized to 0int precision_value; // initialized to 0char 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 objectostream* tie(); // returns ptr to the tied ostreamostream* tie(ostream*); // ties current stream to specified ostreamstatic bool sync_with_stdio(bool sync = true); // sync with C standard I/Oprotected:streambuf* streambuffer; // pointer to a streambuf objectostream* 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 inputout = 0x0002, // open file for outputate = 0x0004, // seek to end when file is openedapp = 0x0008, // open file in append modetrunc = 0x0010, // truncate the file if it existsbinary = 0x0020 // open file in binary mode};enum seek_dir {beg = 0x0100, // seek relative to beginning of filecur = 0x0200, // seek relative to current positionend = 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 objectcin;};
}
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 objectscout, 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 methodsbool good() const; // true if no error flag is setbool eof() const; // true if stream is at end of filebool fail() const; // true if badbit or failbit are setbool bad() const; // true if badbit is setoperator void*() const; // null pointer if fail(), non-null otherwisevoid clear(iostate newstate = goodbit); // sets state to newstatevoid setstate(iostate addstate); // adds addstate to existing stateenum iostate {goodbit = 0x0000, // everything's okeofbit = 0x0001, // stream is at end of filefailbit = 0x0002, // last I/O operation failedbadbit = 0x0004 // serious error, stream unusable};protected:unsigned long state; // stores status bitsunsigned long flags; // stores mode bitsunsigned long mode; // stores flag bitsint width_value; // initialized to 0int precision_value; // initialized to 0char 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 setbool eof() const; // true if eofbit is setbool fail() const; // true if badbit or failbit are setbool bad() const; // true if badbit is setoperator void*() const; // null pointer if fail(), non-null otherwisevoid clear(iostate newstate = goodbit); // sets state to newstatevoid setstate(iostate addstate); // adds addstate to existing stateenum iostate {goodbit = 0x0000, // everything's okeofbit = 0x0001, // stream is at end of filefailbit = 0x0002, // last I/O operation failedbadbit = 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 flagsfmtflags flags(fmtflags newflags); // sets flags to newflagsfmtflags setf(fmtflags setbits); // sets specified flagsfmtflags setf(fmtflags setbits, fmtflags mask); // sets flags in maskfmtflags unsetf(fmtflags unsetbits); // clears specified flagsenum fmtflags {boolalpha = 0x0001, // read/write bool values in alphabeticleft = 0x0002, // left-justify outputright = 0x0004, // right-justify outputinternal = 0x0008, // prefix left..fill..number rightskipws = 0x0010, // skip white space before extractiondec = 0x0020, // decimalhex = 0x0040, // hexadecimaloct = 0x0080, // octalshowbase = 0x0100, // show base indicator on outputshowpoint = 0x0200, // show decimal point for fixed point outputshowpos = 0x0400, // force show of sign for positive numbersfixed = 0x0800, // force decimal notation for floatscientific = 0x1000, // force scientific notation for floatunitbuf = 0x2000, // flush buffer after each insertionuppercase = 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; // 初始化為 0int precision_value; // 初始化為 0char 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*); // 將當前流綁定到指定的 ostreamstatic 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;elsereturn true;}private:int _a;
};int main() {A a1(20);A a2(1);cout << (bool)a1 << endl; // 輸出:falsecout << (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.*/explicitbasic_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_typetellp();/*** @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/swapbasic_ostream& operator=(const basic_ostream&) = delete;basic_ostream&operator=(basic_ostream&& __rhs){swap(__rhs);return *this;}voidswap(basic_ostream& __rhs){ __ios_type::swap(__rhs); }
#endiftemplate<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).*/explicitsentry(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 MTif (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 >= 201103Lexplicit
#endifoperator 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)); }// Specializationtemplate <class _Traits>inline basic_ostream<char, _Traits>&operator<<(basic_ostream<char, _Traits>& __out, char __c){ return __ostream_insert(__out, &__c, 1); }// Signed and unsignedtemplate<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 specializationstemplate<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 unsignedtemplate<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 >= 201103Ltemplate<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>inlinetypename 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>>::typeoperator<<(_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 pointerreturn (_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 inputif (fin.fail()) // blank linefin.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