如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY 4.0 (除特别声明或转载文章外)
六 C++ IO 方式
0、继承体系
1、ios_base
0)四种状态结构
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; ... } }
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; ... } }
**
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; ... } }
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);
可以通过成员函数 flags
和 setf
flag value | effect when set |
---|---|
internal | the output is padded to the field width by inserting fill characters at a specified internal point. |
left | the output is padded to the field width appending fill characters at the end. |
right | the output is padded to the field width by inserting fill characters at the beginning. |
对应的 mask
为 adjustfield
每次 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
控制字符序列的输入输出,提供对下面两种字符序列的访问:
- controlled character sequence 控制字符序列被称为 buffer,可以被分为输入缓冲(get area),输出缓冲(put area)
- associated character sequence 被关联的字符序列。它是通过 OS API 获取到的实体。这个实体可以是文件,网络套接字,或者是 vector,string 对象
控制字符序列是 charT 类型的数组,是关联字符序列的子序列 或 窗口(window) 。其状态由 3 个指针描述:
- beginning 指针,指向缓冲区的最低元素(lowest element,可以理解为开始)
- next 指针,指向下一个读/写的元素
- end 指针,指向缓冲的尾后(最后一个元素后的位置)
basic_streambuf
对象可能支持输入(被 begin,next,end 描述的 buffer 被称为 get area);或者输出(put area);或者同时支持两者。最后一种情况,6 个指针被追踪,这些指针可能指向同一个或不同的字符数组。
Public Menber Functions
Positioning | |
---|---|
pubsetbuf | invokes setbuf() (public member function) |
pubseekoff | invokes seekoff() (public member function) |
pubseekpos | invokes seekpos() (public member function) |
pubsync | invokes sync() (public member function) |
Get area | |
in_avail | obtains the number of characters immediately available in the get area (public member function) |
snextc | advances 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) |
sgetc | reads one character from the input sequence without advancing the sequence (public member function) |
sgetn | invokes xsgetn() (public member function) |
Put area | |
sputc | writes one character to the put area and advances the next pointer (public member function) |
sputn | invokes xsputn() (public member function) |
Putback | |
sputbackc | puts one character back in the input sequence (public member function) |
sungetc | moves 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) |
ebackgptregptr | returns a pointer to the beginning, current character and the end of the get area (protected member function) |
gbump | advances the next pointer in the input sequence (protected member function) |
setg | repositions 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) |
pbasepptrepptr | returns a pointer to the beginning, current character and the end of the put area (protected member function) |
pbump | advances the next pointer of the output sequence (protected member function) |
setp | repositions 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
类型的参数,一般会用到的值类似 stdio
中 fseek
的 START,CUR,END 标志位。off_type off
,off
是根据该标志计算偏移,可以理解为指针指向 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 指针
sputc
,sputn
调用此函数时,pptr() == nullptr
或 pptr() >= 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(); }
- 当 get area 中没有 putback 位置的时候(pbackfail 没有参数),pbackfail 备份 get area,如果 associated sequence 允许这么做。(file streambuf 可能会重新从文件中读取字符到 buffer 中,这次会提前一个字符。)
- 当调用者尝试放回一个和之前获取到的字符不同的字符(
pbackfail()
被调用时有需要放回的字符)。这种情况下,pbackfail 会用形参__c
替换 next 指针指向的字符的前一个位置上的字符。如果可能,会修改 associated sequence。这个操作可能会像情况 1 那样引起备份。
注意:ANSI C(std c) 中的 ungetc
函数不会修改和 get area 关联的源(比如文件。)
[C++ 之定制输入输出流 - Kaiyuan’s Blog May 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;
};