IO详解五 纵观 C++ 文件 IO 的继承体系 Shepard-Wang

六 C++ IO 方式

0、继承体系

io15.png

io16.png

1、ios_base

0)四种状态结构

  1. flag 流的格式控制

    namespace std
    {
        enum _Ios_Fmtflags
        {
            _S_boolalpha 	= 1L << 0,
            _S_dec 		= 1L << 1,
            _S_fixed 		= 1L << 2,
            _S_hex 		= 1L << 3,
            _S_internal 	= 1L << 4,
            _S_left 		= 1L << 5,
            _S_oct 		= 1L << 6,
            _S_right 		= 1L << 7,
            _S_scientific 	= 1L << 8,
            _S_showbase 	= 1L << 9,
            _S_showpoint 	= 1L << 10,
            _S_showpos 	= 1L << 11,
            _S_skipws 	= 1L << 12,
            _S_unitbuf 	= 1L << 13,
            _S_uppercase 	= 1L << 14,
            _S_adjustfield 	= _S_left | _S_right | _S_internal,
            _S_basefield 	= _S_dec | _S_oct | _S_hex,
            _S_floatfield 	= _S_scientific | _S_fixed,
            _S_ios_fmtflags_end = 1L << 16,
            _S_ios_fmtflags_max = __INT_MAX__,
            _S_ios_fmtflags_min = ~__INT_MAX__
        };
       
        inline _GLIBCXX_CONSTEXPR _Ios_Fmtflags
        operator&(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
        { return _Ios_Fmtflags(static_cast<int>(__a) & static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Fmtflags
        operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
        { return _Ios_Fmtflags(static_cast<int>(__a) | static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Fmtflags
        operator^(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
        { return _Ios_Fmtflags(static_cast<int>(__a) ^ static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Fmtflags
        operator~(_Ios_Fmtflags __a)
        { return _Ios_Fmtflags(~static_cast<int>(__a)); }
       
        inline const _Ios_Fmtflags&
        operator|=(_Ios_Fmtflags& __a, _Ios_Fmtflags __b)
        { return __a = __a | __b; }
       
        inline const _Ios_Fmtflags&
        operator&=(_Ios_Fmtflags& __a, _Ios_Fmtflags __b)
        { return __a = __a & __b; }
       
        inline const _Ios_Fmtflags&
        operator^=(_Ios_Fmtflags& __a, _Ios_Fmtflags __b)
        { return __a = __a ^ __b; }
       
       
        class ios_base
        {
        public:
            ...
        	typedef _Ios_Fmtflags fmtflags;
       
            /// Insert/extract @c bool in alphabetic rather than numeric format.
            static const fmtflags boolalpha =   _S_boolalpha;
       
            /// Converts integer input or generates integer output in decimal base.
            static const fmtflags dec =         _S_dec;
       
            /// Generate floating-point output in fixed-point notation.
            static const fmtflags fixed =       _S_fixed;
       
            /// Converts integer input or generates integer output in hexadecimal base.
            static const fmtflags hex =         _S_hex;
       
            /// Adds fill characters at a designated internal point in certain
            /// generated output, or identical to @c right if no such point is
            /// designated.
            static const fmtflags internal =    _S_internal;
       
            /// Adds fill characters on the right (final positions) of certain
            /// generated output.  (I.e., the thing you print is flush left.)
            static const fmtflags left =        _S_left;
       
            /// Converts integer input or generates integer output in octal base.
            static const fmtflags oct =         _S_oct;
       
            /// Adds fill characters on the left (initial positions) of certain
            /// generated output.  (I.e., the thing you print is flush right.)
            static const fmtflags right =       _S_right;
       
            /// Generates floating-point output in scientific notation.
            static const fmtflags scientific =  _S_scientific;
       
            /// Generates a prefix indicating the numeric base of generated integer
            /// output.
            static const fmtflags showbase =    _S_showbase;
       
            /// Generates a decimal-point character unconditionally in generated
            /// floating-point output.
            static const fmtflags showpoint =   _S_showpoint;
       
            /// Generates a + sign in non-negative generated numeric output.
            static const fmtflags showpos =     _S_showpos;
       
            /// Skips leading white space before certain input operations.
            static const fmtflags skipws =      _S_skipws;
       
            /// Flushes output after each output operation.
            static const fmtflags unitbuf =     _S_unitbuf;
       
            /// Replaces certain lowercase letters with their uppercase equivalents
            /// in generated output.
            static const fmtflags uppercase =   _S_uppercase;
       
            /// A mask of left|right|internal.  Useful for the 2-arg form of @c setf.
            static const fmtflags adjustfield = _S_adjustfield;
       
            /// A mask of dec|oct|hex.  Useful for the 2-arg form of @c setf.
            static const fmtflags basefield =   _S_basefield;
       
            /// A mask of scientific|fixed.  Useful for the 2-arg form of @c setf.
            static const fmtflags floatfield =  _S_floatfield;        
            ...
        }
    }
    
  2. mode 文件打开的方式

    namespace std
    {
    	enum _Ios_Openmode
        {
            _S_app 		= 1L << 0,
            _S_ate 		= 1L << 1,
            _S_bin 		= 1L << 2,
            _S_in 		= 1L << 3,
            _S_out 		= 1L << 4,
            _S_trunc 		= 1L << 5,
            _S_ios_openmode_end = 1L << 16,
            _S_ios_openmode_max = __INT_MAX__,
            _S_ios_openmode_min = ~__INT_MAX__
        };
       
        inline _GLIBCXX_CONSTEXPR _Ios_Openmode
        operator&(_Ios_Openmode __a, _Ios_Openmode __b)
        { return _Ios_Openmode(static_cast<int>(__a) & static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Openmode
        operator|(_Ios_Openmode __a, _Ios_Openmode __b)
        { return _Ios_Openmode(static_cast<int>(__a) | static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Openmode
        operator^(_Ios_Openmode __a, _Ios_Openmode __b)
        { return _Ios_Openmode(static_cast<int>(__a) ^ static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Openmode
        operator~(_Ios_Openmode __a)
        { return _Ios_Openmode(~static_cast<int>(__a)); }
       
        inline const _Ios_Openmode&
        operator|=(_Ios_Openmode& __a, _Ios_Openmode __b)
        { return __a = __a | __b; }
       
        inline const _Ios_Openmode&
        operator&=(_Ios_Openmode& __a, _Ios_Openmode __b)
        { return __a = __a & __b; }
       
        inline const _Ios_Openmode&
        operator^=(_Ios_Openmode& __a, _Ios_Openmode __b)
        { return __a = __a ^ __b; }
       
       
        class ios_base
        {
        public:
            ...
                typedef _Ios_Openmode openmode;
       
            /// Seek to end before each write.
            static const openmode app =		_S_app;
       
            /// Open and seek to end immediately after opening.
            static const openmode ate =		_S_ate;
       
            /// Perform input and output in binary mode (as opposed to text mode).
            /// This is probably not what you think it is; see
            /// https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html #std.io.filestreams.binary
            static const openmode binary =	_S_bin;
       
            /// Open for input.  Default for @c ifstream and fstream.
            static const openmode in =		_S_in;
       
            /// Open for output.  Default for @c ofstream and fstream.
            static const openmode out =		_S_out;
       
            /// Open for input.  Default for @c ofstream.
            static const openmode trunc =	_S_trunc;
            ...
        }
    }
    
  3. **state ** 文件状态

    namespace std
    {
    	enum _Ios_Iostate
        {
            _S_goodbit 		= 0,
            _S_badbit 		= 1L << 0,
            _S_eofbit 		= 1L << 1,
            _S_failbit		= 1L << 2,
            _S_ios_iostate_end = 1L << 16,
            _S_ios_iostate_max = __INT_MAX__,
            _S_ios_iostate_min = ~__INT_MAX__
        };
       
        inline _GLIBCXX_CONSTEXPR _Ios_Iostate
        operator&(_Ios_Iostate __a, _Ios_Iostate __b)
        { return _Ios_Iostate(static_cast<int>(__a) & static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Iostate
        operator|(_Ios_Iostate __a, _Ios_Iostate __b)
        { return _Ios_Iostate(static_cast<int>(__a) | static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Iostate
        operator^(_Ios_Iostate __a, _Ios_Iostate __b)
        { return _Ios_Iostate(static_cast<int>(__a) ^ static_cast<int>(__b)); }
       
        inline _GLIBCXX_CONSTEXPR _Ios_Iostate
        operator~(_Ios_Iostate __a)
        { return _Ios_Iostate(~static_cast<int>(__a)); }
       
        inline const _Ios_Iostate&
        operator|=(_Ios_Iostate& __a, _Ios_Iostate __b)
        { return __a = __a | __b; }
       
        inline const _Ios_Iostate&
        operator&=(_Ios_Iostate& __a, _Ios_Iostate __b)
        { return __a = __a & __b; }
       
        inline const  _Ios_Iostate&
        operator^=(_Ios_Iostate& __a, _Ios_Iostate __b)
        { return __a = __a ^ __b; }
       
        class ios_base
        {
        public:
            ...
        	typedef _Ios_Iostate iostate;
       
            /// Indicates a loss of integrity in an input or output sequence (such
            /// as an irrecoverable read error from a file).
            static const iostate badbit =	_S_badbit;
       
            /// Indicates that an input operation reached the end of an input sequence.
            static const iostate eofbit =	_S_eofbit;
       
            /// Indicates that an input operation failed to read the expected
            /// characters, or that an output operation failed to generate the
            /// desired characters.
            static const iostate failbit =	_S_failbit;
       
            /// Indicates all is well.
            static const iostate goodbit =	_S_goodbit;
            ...
        }
    }
    
  4. seek 流指针起始位置

    namespace std
    {
    	enum _Ios_Seekdir
        {
            _S_beg = 0,
            _S_cur = _GLIBCXX_STDIO_SEEK_CUR,
            _S_end = _GLIBCXX_STDIO_SEEK_END,
            _S_ios_seekdir_end = 1L << 16
        };
       
       
        class ios_base
        {
        public:
            ...
    		typedef _Ios_Seekdir seekdir;
       
            /// Request a seek relative to the beginning of the stream.
            static const seekdir beg =		_S_beg;
       
            /// Request a seek relative to the current position within the sequence.
            static const seekdir cur =		_S_cur;
       
            /// Request a seek relative to the current end of the sequence.
            static const seekdir end =		_S_end;
            ...
        }
    }
    

1)precision

精度控制,对于 precison(5) 的调用,下列选项的含义:

  • default:默认的,什么也不指定。最多显示 5 个数
  • fixed:最少显示 5 个数
  • scientific:使用科学计数法,最少显示 10 位数
#include <iostream>     // std::cout, std::ios

int main () {
    double f = 30.14159;
    std::cout.unsetf ( std::ios::floatfield );                // floatfield not set

    std::cout.precision(5);
    std::cout << f << '\n';

    std::cout.precision(10);
    std::cout << f << '\n';

    std::cout.setf( std::ios::fixed ); // floatfield set to fixed
    std::cout << f << '\n';
    
    // 需要使用 mask 去掉 ios::fixed
    std::cout.setf( std::ios::scientific, std::ios::floatfield );
    std::cout << f << '\n';
    return 0;
}

2)width

输出的字段长度控制

get (1)	
streamsize width() const;
set (2)	
streamsize width (streamsize wide);

可以通过成员函数 flagssetf

flag valueeffect when set
internalthe output is padded to the field width by inserting fill characters at a specified internal point.
leftthe output is padded to the field width appending fill characters at the end.
rightthe output is padded to the field width by inserting fill characters at the beginning.

对应的 maskadjustfield

每次 cout 都必须重新设置 width

int main () {
    std::cout << 100 << '\n';

    std::cout.width(10);
    std::cout << 100 << '\n';

    std::cout.fill('x');
    std::cout.width(15);
    std::cout << std::left << 100 << '\n';

    std::cout.width(15);
    std::cout.setf(std::ios::right, std::ios::adjustfield);
    std::cout << 100 << '\n';

    return 0;
}

3)源码

除去了一些比较难懂的结构


2、basic_ios

3、basic_streambuf

Defined in header <streambuf>

template<class CharT, 
		 class Traits = std::char_traits<CharT> > 
class basic_streambuf;

The I/O stream objects std::basic_istream and std::basic_ostream, as well as all objects derived from them (std::ofstream, std::stringstream, etc), are implemented entirely in terms of std::basic_streambuf.

basic_streambuf 控制字符序列的输入输出,提供对下面两种字符序列的访问:

  1. controlled character sequence 控制字符序列被称为 buffer,可以被分为输入缓冲(get area),输出缓冲(put area)
  2. associated character sequence 被关联的字符序列。它是通过 OS API 获取到的实体。这个实体可以是文件,网络套接字,或者是 vector,string 对象

控制字符序列是 charT 类型的数组,是关联字符序列的子序列窗口(window) 。其状态由 3 个指针描述:

  1. beginning 指针,指向缓冲区的最低元素(lowest element,可以理解为开始)
  2. next 指针,指向下一个读/写的元素
  3. end 指针,指向缓冲的尾后(最后一个元素后的位置)

basic_streambuf 对象可能支持输入(被 begin,next,end 描述的 buffer 被称为 get area);或者输出(put area);或者同时支持两者。最后一种情况,6 个指针被追踪,这些指针可能指向同一个或不同的字符数组。

io17.png

Public Menber Functions

Positioning 
pubsetbufinvokes setbuf() (public member function)
pubseekoffinvokes seekoff() (public member function)
pubseekposinvokes seekpos() (public member function)
pubsyncinvokes sync() (public member function)
Get area 
in_availobtains the number of characters immediately available in the get area (public member function)
snextcadvances the input sequence, then reads one character without advancing again (public member function)
sbumpcstossc(removed in C++17)reads one character from the input sequence and advances the sequence (public member function)
sgetcreads one character from the input sequence without advancing the sequence (public member function)
sgetninvokes xsgetn() (public member function)
Put area 
sputcwrites one character to the put area and advances the next pointer (public member function)
sputninvokes xsputn() (public member function)
Putback 
sputbackcputs one character back in the input sequence (public member function)
sungetcmoves the next pointer in the input sequence back by one (public member function)
  

Protected member functions

  
(constructor)constructs a basic_streambuf object (protected member function)
operator=(C++11)replaces a basic_streambuf object (protected member function)
swap(C++11)swaps two basic_streambuf objects (protected member function)
Locales 
imbue[virtual]changes the associated locale (virtual protected member function)
Positioning 
setbuf[virtual]replaces the buffer with user-defined array, if permitted (virtual protected member function)
seekoff[virtual]repositions the next pointer in the input sequence, output sequence, or both, using relative addressing (virtual protected member function)
seekpos[virtual]repositions the next pointer in the input sequence, output sequence, or both using absolute addressing (virtual protected member function)
sync[virtual]synchronizes the buffers with the associated character sequence (virtual protected member function)
Get area 
showmanyc[virtual]obtains the number of characters available for input in the associated input sequence, if known (virtual protected member function)
underflow[virtual]reads characters from the associated input sequence to the get area (virtual protected member function)
uflow[virtual]reads characters from the associated input sequence to the get area and advances the next pointer (virtual protected member function)
xsgetn[virtual]reads multiple characters from the input sequence (virtual protected member function)
ebackgptregptrreturns a pointer to the beginning, current character and the end of the get area (protected member function)
gbumpadvances the next pointer in the input sequence (protected member function)
setgrepositions the beginning, next, and end pointers of the input sequence (protected member function)
Put area 
xsputn[virtual]writes multiple characters to the output sequence (virtual protected member function)
overflow[virtual]writes characters to the associated output sequence from the put area (virtual protected member function)
pbasepptrepptrreturns a pointer to the beginning, current character and the end of the put area (protected member function)
pbumpadvances the next pointer of the output sequence (protected member function)
setprepositions the beginning, next, and end pointers of the output sequence (protected member function)
Putback 
pbackfail[virtual]puts a character back into the input sequence, possibly modifying the input sequence (virtual protected member function)

seekoff 和 seekpos 的区别

seekoff 函数调用会加上 std::ios_base::seekdir dir类型的参数,一般会用到的值类似 stdiofseek 的 START,CUR,END 标志位。off_type offoff 是根据该标志计算偏移,可以理解为指针指向 dir + off 的位置(off 可以是负数)

seekpos 则没有 dir 标志位,直接从流开头计算偏移,也就是 START + off

pos_type pubseekoff( off_type off, std::ios_base::seekdir dir,
                     std::ios_base::openmode which = ios_base::in | ios_base::out );

protected:
virtual pos_type seekoff( off_type off, std::ios_base::seekdir dir,
                          std::ios_base::openmode which = ios_base::in | ios_base::out );

pos_type pubseekpos( pos_type pos,
                     std::ios_base::openmode which = std::ios_base::in | std::ios_base::out );
	
protected:
virtual pos_type seekpos( pos_type pos,
                          std::ios_base::openmode which = std::ios_base::in | std::ios_base::out);

underflow

从 associated input sequence 中读取字符到 get area 中。

Ensures that at least one character is available in the input area by updating the pointers to the input area (if needed) and reading more data in from the input sequence (if applicable).

virtual int_type
underflow()
{ return traits_type::eof(); }
#include <iostream>
#include <sstream>
 
class null_filter_buf : public std::streambuf {
    std::streambuf* src;
    char ch; // single-byte buffer
protected:
    int underflow() {
        traits_type::int_type i;
        while ((i = src->sbumpc()) == '\0') ; // skip zeroes
        if (!traits_type::eq_int_type(i, traits_type::eof())) {
            ch = traits_type::to_char_type(i);
            setg(&ch, &ch, &ch+1); // make one read position available
        }
        reEturn i;
    }
public:
    null_filter_buf(std::streambuf* buf) : src(buf) {
        setg(&ch, &ch+1, &ch+1); // buffer is initially full
    }
};
 
void filtered_read(std::istream& in)
{
    std::streambuf* orig = in.rdbuf();
    null_filter_buf buf(orig);
    in.rdbuf(&buf);
    for(char c; in.get(c); )
            std::cout << c;
    in.rdbuf(orig);
}
 
int main()
{
    char a[] = "This i\0s \0an e\0\0\0xample";
    std::istringstream in(std::string(std::begin(a), std::end(a)));
    filtered_read(in);
}

uflow

从 associated input sequence 中读取字符到 get area 中。并且将 next 指针前移一位(读取一位)。

Ensures that at least one character is available in the input area by updating the pointers to the input area (if needed).

virtual int_type
    uflow()
{
    int_type __ret = traits_type::eof();
    const bool __testeof = traits_type::eq_int_type(this->underflow(),
                                                    __ret);
    if (!__testeof)
    {
        __ret = traits_type::to_int_type(*this->gptr());
        this->gbump(1);
    }
    return __ret;
}

xgetn

template<typename _CharT, typename _Traits>
streamsize
basic_streambuf<_CharT, _Traits>::
xsgetn(char_type* __s, streamsize __n)
{
    streamsize __ret = 0;
    
    while (__ret < __n)
    {
        const streamsize __buf_len = this->egptr() - this->gptr();
        // 尝试读取 __n 个字符
        if (__buf_len)
        {
            const streamsize __remaining = __n - __ret;
            const streamsize __len = std::min(__buf_len, __remaining);
            traits_type::copy(__s, this->gptr(), __len);
            __ret += __len;
            __s += __len;
            this->__safe_gbump(__len);
        }
		// get area 读完,尝试调用 uflow 更新 get area
        // uflow 更新成功后回自动读取一个字符、
        if (__ret < __n)
        {
            const int_type __c = this->uflow();
            if (!traits_type::eq_int_type(__c, traits_type::eof()))
            {
                traits_type::assign(*__s++, traits_type::to_char_type(__c));
                ++__ret;
            }
            // 读到 associated sequence 尾,也就是 eof,直接退出
            else
                break;
        }
        
        // 如果已读取字符数 __ret 还小于 __n 回重新进入 while 循环  
    }
    return __ret;
}

sputc

int_type
sputc(char_type __c)
{
    int_type __ret;
    if (__builtin_expect(this->pptr() < this->epptr(), true))
    {
        *this->pptr() = __c;
        this->pbump(1);
        __ret = traits_type::to_int_type(__c);
    }
    else
        __ret = this->overflow(traits_type::to_int_type(__c));
    return __ret;
}

overflow

virtual int_type
overflow(int_type __c _IsUnused  = traits_type::eof())
{ return traits_type::eof(); }

sync

virtual int
sync() { return 0; }

对于输出流而言,一般的结果是将 get area 中的内容写入 associated sequence,比如刷新输出缓冲。对于输入流来说,一般的应用是清空 get area 然后强制从 associated sequence 中重新读取最近的改变。std::basic_stringbuf默认的行为是什么也不做。

showmanyc

检测 associated sequence 中有还有多少字符可用于输入(get area)。

virtual streamsize
    showmanyc() { return 0; }

如果 showmanyc 返回 -1,无阻塞时,表示没有字符可用。underflow()uflow() 一定会返回 Traits::eof或者抛出异常。

Notes:

The name of this function stands for “stream: how many characters?”, so it is pronounced “S how many C”, rather than “show many C”

gbump

将 next 指针前移 n 位。没有 underflow 检查(越界检查)。

void
gbump(int __n) { _M_in_cur += __n; }

overflow

将 put area 中的字符写入 associated input sequence

可能会更新 beg,next,end 指针

sputcsputn调用此函数时,pptr() == nullptrpptr() >= epptr()

#include <iostream>
#include <array>
 
// Buffer for std::ostream implemented by std::array
template<std::size_t SIZE, class CharT = char>
class ArrayedStreamBuffer : public std::basic_streambuf<CharT> {
public:
 
    using Base = std::basic_streambuf<CharT>;
    using char_type = typename Base::char_type;
    using int_type = typename Base::int_type;
 
    ArrayedStreamBuffer() : buffer_{} // value-initialize buffer_ to all zeroes
    {
        Base::setp(buffer_.begin(), buffer_.end()); // set std::basic_streambuf
            // put area pointers to work with 'buffer_'
    }
 
    int_type overflow(int_type ch) 
    {
        std::cout << "overflow\n";
        return Base::overflow(ch);
    }
 
    void print_buffer()
    {
        for (const auto& i: buffer_) {
            if (i == 0) {
                std::cout << "\\0";
            } else {
                std::cout << i;
            }
            std::cout << ' ';
        }
        std::cout << '\n';
    }
 
private:
    std::array<char_type, SIZE> buffer_;
};
 
int main()
{
    ArrayedStreamBuffer<10> streambuf;
    std::ostream stream(&streambuf);
 
    stream << "hello";
    streambuf.print_buffer();
    if (stream.good()) {
        std::cout << "stream is good\n";
    }
 
    stream << "world";
    streambuf.print_buffer();
    if (stream.good()) {
        std::cout << "stream is good\n";
    }
 
    stream << "!";
    streambuf.print_buffer();
    if (!stream.good()) {
        std::cout << "stream is not good\n";
    }
}

Output:

h e l l o \0 \0 \0 \0 \0
stream is good
h e l l o w o r l d 
stream is good
overflow
h e l l o w o r l d 
stream is not good

setg & setp

void
setp(char_type* __pbeg, char_type* __pend)
{
    _M_out_beg = _M_out_cur = __pbeg;
    _M_out_end = __pend;
}

void
setg(char_type* __gbeg, char_type* __gnext, char_type* __gend)
{
    _M_in_beg = __gbeg;
    _M_in_cur = __gnext;
    _M_in_end = __gend;
}

sputback & sungetc

sputbackc

将字符放回到 get area 中

如果成功,返回之前的字符。

只有当 next 指针小于 begin 指针,且 get area 中的前一个字符和形参 __c 相同时,将 next 指针减去 1。否则调用 pbackfail()

int_type
sputbackc(char_type __c)
{
    int_type __ret;
    const bool __testpos = this->eback() < this->gptr();
    if (__builtin_expect(!__testpos ||
                         !traits_type::eq(__c, this->gptr()[-1]), false))
        __ret = this->pbackfail(traits_type::to_int_type(__c));
    else
    {
        this->gbump(-1);
        __ret = traits_type::to_int_type(*this->gptr());
    }
    return __ret;
}

调用pbackfail 时会备份 get area 或者同时修改 get area 以及 associated sequence

#include <iostream>
#include <sstream>
 
int main()
{
    std::stringstream s("abcdef"); // gptr() points to 'a' in "abcdef"
    std::cout << "Before putback, string holds " << s.str() << '\n';
    char c1 = s.get(); // c1 = 'a', gptr() now points to 'b' in "abcdef"
    char c2 = s.rdbuf()->sputbackc('z'); // same as s.putback('z')
                                         // gptr() now points to 'z' in "zbcdef"
    std::cout << "After putback, string holds " << s.str() << '\n';
    char c3 = s.get(); // c3 = 'z', gptr() now points to 'b' in "zbcdef"
    char c4 = s.get(); // c4 = 'b', gptr() now points to 'c' in "zbcdef"
    std::cout << c1 << c2 << c3 << c4 << '\n';
 
    s.rdbuf()->sputbackc('b');  // gptr() now points to 'b' in "zbcdef"
    s.rdbuf()->sputbackc('z');  // gptr() now points to 'z' in "zbcdef"
    int eof = s.rdbuf()->sputbackc('x');  // nothing to unget: pbackfail() fails
    if (eof == EOF)
        std::cout << "No room to putback after 'z'\n";
}

Output:

Before putback, string holds abcdef
After putback, string holds zbcdef
azzb
No room to putback after 'z'

sungetc

int_type
    sungetc()
{
    int_type __ret;
    if (__builtin_expect(this->eback() < this->gptr(), true))
    {
        this->gbump(-1);
        __ret = traits_type::to_int_type(*this->gptr());
    }
    else
        __ret = this->pbackfail();
    return __ret;
}

pbackfail

virtual int_type
pbackfail(int_type __c _IsUnused  = traits_type::eof())
{ return traits_type::eof(); }
  1. 当 get area 中没有 putback 位置的时候(pbackfail 没有参数),pbackfail 备份 get area,如果 associated sequence 允许这么做。(file streambuf 可能会重新从文件中读取字符到 buffer 中,这次会提前一个字符。)
  2. 当调用者尝试放回一个和之前获取到的字符不同的字符(pbackfail() 被调用时有需要放回的字符)。这种情况下,pbackfail 会用形参 __c 替换 next 指针指向的字符的前一个位置上的字符。如果可能,会修改 associated sequence。这个操作可能会像情况 1 那样引起备份。

注意:ANSI C(std c) 中的 ungetc 函数不会修改和 get area 关联的源(比如文件。)

  • [C++ 之定制输入输出流 - Kaiyuan’s BlogMay the force be with me](http://kaiyuan.me/2017/06/22/custom-streambuf/)

tcp buffer

class tcpbuf : public std::streambuf {
    void initsocklib() {
#ifdef WIN32
        static bool inited = false;
        WSADATA wsaData;
        if (!inited)
            WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
    }
public:
    enum { BUFSIZE = 1 << 5 };
    tcpbuf(SOCKET s) { initsocklib(); sock = s; }
    tcpbuf() {
        initsocklib();
        sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    }
    ~tcpbuf() override { close(); }

    bool connect(char *ip, unsigned short port) {
        sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_family = AF_INET;
        addr.sin_port = ::htons(port);
        return !::connect(sock, (sockaddr *)&addr, sizeof(addr));
    }
    int close() {
#ifdef WIN32
        return ::closesocket(sock);
#else
        return ::close(sock);
#endif
    }

protected:
    // Buffered get
    int underflow() override {
        auto n = ::recv(sock, buf, BUFSIZE, 0);
        return n > 0 ? (setg(buf, buf, buf + n), *gptr()) : EOF;
    }
    // Unbuffered put
    int overflow(int c) override {
        if (c == EOF) return close();
        char b = c;
        return ::send(sock, &b, 1, 0) > 0 ? c : EOF;
    }
    std::streamsize xsputn(const char *s, std::streamsize n) override {
        auto x = ::send(sock, s, n, 0);
        return x > 0 ? x : 0;
    }
    // flush
    int sync() override {
#ifdef WIN32
        return 0;
#else
        return flush(sock);
#endif // WIN32
    }
    //streamsize showmanyc() override { return 1; }

private:
    SOCKET sock;
    char buf[BUFSIZE];
};

class tstream : public std::iostream {
public:
    tstream() : std::iostream(&_buf) {}
    tstream(SOCKET sock) : _buf(sock), std::iostream(&_buf) {}
    tstream(char *ip, unsigned short port)
        : tstream() { connect(ip, port); }

    bool connect(char *ip, unsigned short port) {
        return _buf.connect(ip, port);
    }
private:
    tcpbuf _buf;
};