本库包含标准 I/O 库的各种实用程序。
IO 状态保存器,<boost/io/ios_state.hpp>
描述
头文件 <boost/io/ios_state.hpp>
涵盖了 C++ IOStreams 系统中对象的流状态保存。
基本原理
有时某个值只能在有限的范围内更改。保存器类会保存某个对象(或对象的某个方面)当前状态的副本,并在销毁时重置对象的状态,撤销对象可能经历的任何更改。
保存器类策略在使用 I/O 流对象时很有帮助。操纵符对象可以在输入或输出期间更改流的某些方面。操纵符改变的状态通常在 I/O 事务结束后仍保持其新值。如果操纵符在某个函数中使用,而该函数不应从外部更改流的状态,这可能会成为一个问题。
#include <ostream>
#include <ios>
void hex_my_byte(std::ostream& os, char byte)
{
os << std::hex << static_cast<unsigned>(byte);
}
调用 hex_my_byte
后,os
流将保留其新的十六进制打印模式。可以通过手动调用流的状态检查和修改成员函数来保存和恢复流的打印模式。如果主要功能复杂且/或需要异常安全,手动方法会变得繁琐。保存器类可以实现更好的“资源获取即初始化”策略。
使用保存器类以获得更好的代码,请参见下面的示例。
头文件概要
namespace boost {
namespace io {
class ios_flags_saver;
class ios_precision_saver;
class ios_width_saver;
class ios_base_all_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_iostate_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_exception_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_tie_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_rdbuf_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_fill_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_locale_saver;
template<class Ch, class Tr = std::char_traits<Ch> >
class basic_ios_all_saver;
typedef basic_ios_iostate_saver<char> ios_iostate_saver;
typedef basic_ios_iostate_saver<wchar_t> wios_iostate_saver;
typedef basic_ios_exception_saver<char> ios_exception_saver;
typedef basic_ios_exception_saver<wchar_t> wios_exception_saver;
typedef basic_ios_tie_saver<char> ios_tie_saver;
typedef basic_ios_tie_saver<wchar_t> wios_tie_saver;
typedef basic_ios_rdbuf_saver<char> ios_rdbuf_saver;
typedef basic_ios_rdbuf_saver<wchar_t> wios_rdbuf_saver;
typedef basic_ios_fill_saver<char> ios_fill_saver;
typedef basic_ios_fill_saver<wchar_t> wios_fill_saver;
typedef basic_ios_locale_saver<char> ios_locale_saver;
typedef basic_ios_locale_saver<wchar_t> wios_locale_saver;
typedef basic_ios_all_saver<char> ios_all_saver;
typedef basic_ios_all_saver<wchar_t> wios_all_saver;
class ios_iword_saver;
class ios_pword_saver;
class ios_all_word_saver;
} // io
} // boost
基本标准属性保存器
基本保存器类的格式如下:
class saver {
public:
typedef std::ios_base state_type;
typedef implementation_defined
aspect_type;
explicit saver(state_type& s);
saver(state_type& s, const aspect_type& new_value);
~saver();
void restore();
};
state_type
是 IOStreams 基类 std::ios_base
。用户通常会将实际的输入、输出或组合流对象作为 state-type 参数,而不是基类对象。第一个构造函数接受一个流对象,并保存对流的引用以及特定流属性的当前值。第二个构造函数的工作方式与第一个类似,并使用其第二个参数将流的属性更改为给定的新的 aspect_type
值。析构函数将流的属性恢复到保存的值。可以通过 restore
成员函数提前(并频繁)激活恢复。
类别 | 已保存的属性 | 属性类型 | 读取方法 | 写入方法 |
---|---|---|---|---|
|
格式控制标志 |
|
|
|
|
小数点后的数字位数 |
|
|
|
|
打印对象的最小字段宽度 |
|
|
|
高级标准属性保存器
保存器类模板的格式如下:
template<class Ch, class Tr>
class saver {
public:
typedef std::basic_ios<Ch, Tr> state_type;
typedef implementation-defined
aspect_type;
explicit saver(state_type& s);
saver(state_type& s, const aspect_type& new_value);
~saver();
void restore();
};
state_type
是 IOStreams 基类模板 std::basic_ios<Ch, Tr>
的一个版本,其中 Ch
是字符类型,Tr
是字符特性类。用户通常会将实际的输入、输出或组合流对象作为 state-type 参数,而不是基类对象。第一个构造函数接受一个流对象,并保存对流的引用以及特定流属性的当前值。第二个构造函数的工作方式与第一个类似,并使用其第二个参数将流的属性更改为给定的新的 aspect_type
值。析构函数将流的属性恢复到保存的值。可以通过 restore
成员函数提前(并频繁)激活恢复。
类别 | 已保存的属性 | 属性类型 | 读取方法 | 写入方法 |
---|---|---|---|---|
|
流的故障状态 [1], [2] |
|
|
|
|
触发异常的故障状态 [1] |
|
|
|
|
与流同步的输出流 |
|
|
|
|
与流关联的流缓冲区 [2] |
|
|
|
|
用于填充过大字段宽度的字符 |
|
|
|
|
与流关联的区域信息 [3] |
|
|
|
注意
-
当故障状态标志和/或故障状态异常监视标志发生更改时,如果两组标志中出现匹配,则会抛出异常。这可能意味着这些类模板的构造函数或析构函数可能会抛出异常。
-
当关联的流缓冲区发生更改时,如果给定的流缓冲区的地址非 NULL,则流的故障状态集将重置为“good”,但如果该地址为 NULL,则设置“bad”故障状态。这意味着如果流去除了关联的流缓冲区,则已保存的“good”故障状态可能会恢复为“bad”。更糟糕的是,给定 NULL 流缓冲区地址,如果正在监视“bad”故障状态,则会抛出异常。这可能意味着这些类模板的构造函数或析构函数可能会抛出异常。
-
区域保存器使用
std::basic_ios<Ch, Tr>
类来提取其信息,尽管它也可以使用std::ios_base
中的功能。问题在于ios_base
中所需成员函数的版本与basic_ios
中的版本并非多态相关。将与保存器类一起使用的流类应使用它们通过继承最接近的成员函数的版本,这意味着使用basic_ios
中的版本。
用户自定义属性保存器
有三个类(模板)用于组合属性保存器。ios_base_all_saver
保存器类结合了所有基本属性保存器类的功能。它有一个构造函数,接受需要保存其状态的流。basic_ios_all_saver
结合了所有高级属性保存器类模板和组合的基本属性保存器类的功能。它有一个构造函数,接受需要保存其状态的流。ios_all_word_saver
保存器类组合了保存用户自定义格式化信息的保存器类。其构造函数接受需要保存其属性的流以及用户自定义属性的索引。每个类的析构函数都会恢复已保存的状态。对于具有 restore 成员函数的类,可以提前(并频繁)激活恢复。
示例
Rationale 中使用的代码可以在两个地方进行改进。打印函数可以使用一个保存器来包装更改格式化状态的代码。或者调用函数可以围绕调用用一个保存器。或者两者都可以做,特别是如果用户不知道打印函数是否使用了状态保存器。如果用户希望进行一系列来回更改,而无需将每次更改包装在单独的块中,则可以在每次尝试之间调用 restore 成员函数。
#include <boost/io/ios_state.hpp>
#include <ios>
#include <iostream>
#include <ostream>
void new_hex_my_byte(std::ostream& os, char byte)
{
boost::io::ios_flags_saver ifs(os);
os << std::hex << static_cast<unsigned>(byte);
}
int main()
{
// ...
{
boost::io::ios_all_saver ias(std::cout);
new_hex_my_byte(std::cout, 'A');
}
// ...
{
boost::io::ios_all_saver ias(std::cerr);
new_hex_my_byte(std::cerr, 'b');
ias.restore();
new_hex_my_byte(std::cerr, 'C');
}
// ...
}
鸣谢
Daryle Walker
启动了该库。贡献了格式标志、精度、宽度和用户自定义格式标志保存器类的初始版本。贡献了成功状态、成功状态异常标志、输出流 tie、流缓冲区、字符填充和区域保存器类模板的初始版本。贡献了组合属性类和类模板。贡献了测试文件 ios_state_test.cpp
。
历史
2019 年 12 月 20 日
Glen Fernandes 使所有保存器类不可复制。
2005 年 2 月 28 日
Daryle Walker 根据 Gennadiy Rozental 和 Rob Stewart 的建议添加了 restore 成员函数。
2002 年 3 月 13 日
Daryle Walker 实现的初始版本。
带引号的操纵符,<boost/io/quoted.hpp>
介绍
C++ 标准库流 I/O 处理包含嵌入空格的字符串可能产生意外结果。例如,
std::stringstream ss;
std::string original = "fooled you";
std::string roundtrip;
ss << original;
ss >> roundtrip;
std::cout << original; // outputs: fooled you
std::cout << roundtrip; // outputs: fooled
assert(original == roundtrip); // assert will fire
Boost 的带引号的流 I/O 操纵符在输出时用分隔符(默认为双引号("))将字符串括起来,在输入时则剥离分隔符。这确保了包含嵌入空格的字符串能按预期进行往返传输。例如,
std::stringstream ss;
std::string original = "fooled you";
std::string roundtrip;
ss << quoted(original);
ss >> quoted(roundtrip);
std::cout << quoted(original); // outputs: "fooled you"
std::cout << roundtrip; // outputs: fooled you
assert(original == roundtrip); // assert will not fire
如果字符串包含分隔符字符,则在输出时,该字符前面会加上一个转义字符,转义字符本身也是如此。
std::cout << quoted("'Jack & Jill'", '&', '\''); // outputs: '&'Jack && Jill&''
头文件概要
namespace boost {
namespace io {
template<class Char, class Traits, class Alloc>
unspecified-type1
quoted(const std::basic_string<Char, Traits, Alloc>& string,
Char escape='\\', Char delim='\"');
template<class Char>
unspecified-type2
quoted(const Char* string, Char escape='\\', Char delim='\"');
template<class Char, class Traits, class Alloc>
unspecified-type3
quoted(std::basic_string<Char, Traits, Alloc>& string,
Char escape='\\', Char delim='\"');
} // io
} // boost
unspecified-type1、unspecified-type2 和 unspecified-type3 是实现提供的类型,具有实现提供的 operator<<
template<class Char, class Traits>
std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& os,
const unspecified-typeN
& proxy);
- 效果
-
将字符插入
os
-
delim
-
string
中的每个字符。如果待输出的字符等于escape
或delim
(通过operator==
判断),则先输出escape
。 -
delim
-
- 备注
-
string
、escape
和delim
的类型和值与构造proxy
的quoted
函数调用的相应参数相同。 - 返回
-
os
.
unspecified-type3 是一个实现提供的类型,具有一个实现提供的 operator>>
template<class Char, class Traits>
std::basic_istream<Char, Traits>&
operator>>(std::basic_istream<Char, Traits>& is,
const unspecified-type3
& proxy);
- 效果
-
从
os
中提取字符-
如果提取的第一个字符等于
delim
(通过operator==
判断),则:-
关闭
skipws
标志。 -
string.clear()
-
直到遇到未转义的
delim
字符或is.not_good()
,从os
中提取字符并追加到 string,不同的是,如果遇到转义字符,则忽略它并向 string 追加下一个字符。 -
丢弃最后的
delim
字符。 -
将
skipws
标志恢复到其原始值。
-
-
否则,
os >> string
。
-
- 备注
-
string
、escape
和delim
的类型和值与构造proxy
的quoted
函数调用的相应参数相同。 - 返回
-
是
.
致谢
quoted()
流操纵符源于 Boost 开发者邮件列表的讨论。参与者包括 Beman Dawes、Rob Stewart、Alexander Lamaison、Eric Niebler、Vicente Botet、Andrey Semashev、Phil Richards 和 Rob Murray。Eric Niebler 的建议为模板的名称和形式奠定了基础。
Beman Dawes 将 quoted()
实现为私有详细头文件。Glen Fernandes 更新了实现并将其公开。
Glen Fernandes 修正了实现,以正确考虑流宽度和填充,并对其进行了优化,使其直接写入流缓冲区。
分隔符迭代器,<boost/io/ostream_joiner.hpp>
描述
头文件 <boost/io/ostream_joiner.hpp>
提供了类模板 boost::io::ostream_joiner
,它是一个输出迭代器,用于将对象写入 std::basic_ostream
,并用分隔符分隔。它是 Library Fundamentals TS std::ostream_joiner
的实现,支持 C++03 及更高版本。
示例
以下程序将向量的内容写入标准输出,每个元素用逗号分隔。
#include <boost/io/ostream_joiner.hpp>
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
v.push_back(2);
v.push_back(4);
v.push_back(6);
v.push_back(8);
std::copy(v.begin(), v.end(), boost::make_ostream_joiner(std::cout, ','));
}
参考
头文件概要
namespace boost {
namespace io {
template<class Delim, class Char = char,
class Traits = std::char_traits<Char> >
class ostream_joiner {
public:
typedef Char char_type;
typedef Traits traits_type;
typedef std::basic_ostream<Char, Traits> ostream_type;
typedef std::output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
ostream_joiner(ostream_type& output, const Delim& delim);
ostream_joiner(ostream_type& output, Delim&& delim);
template<class T>
ostream_joiner& operator=(const T& value);
ostream_joiner& operator*() noexcept;
ostream_joiner& operator++() noexcept;
ostream_joiner& operator++(int) noexcept;
};
template<class Char, class Traits, class Delim>
ostream_joiner<std::decay_t<Delim>, Char, Traits>
make_ostream_joiner(std::basic_ostream<Char, Traits>& output, Delim&& delim);
} // io
} // boost
构造函数
ostream_joiner(ostream_type& output, const Delim& delim);
- 效果
-
用
std::addressof(output)
初始化存储的流引用,用delim
初始化存储的分隔符。
ostream_joiner(ostream_type& output, Delim&& delim);
- 效果
-
用
std::addressof(output)
初始化存储的流引用,用std::move(delim)
初始化存储的分隔符。
成员函数
template<class T>
ostream_joiner& operator=(const T& value);
- 效果
-
如果这是该成员函数的第一次调用,则将存储的分隔符写入存储的流引用。将
value
写入存储的流引用。 - 返回
-
*this
.
ostream_joiner& operator*() noexcept;
ostream_joiner& operator++() noexcept;
ostream_joiner& operator++(int) noexcept;
- 返回
-
*this
.
自由函数
template<class Char, class Traits, class Delim>
ostream_joiner<decay_t<Delim>, Char, Traits>
make_ostream_joiner(std::basic_ostream<Char, Traits>& output, Delim&& delim);
- 返回
-
ostream_joiner<std::decay_t<Delim>, Char, Traits>(output, std::forward<Delim>(delim))
.
致谢
Glen Fernandes 实现的 ostream_joiner
和 make_ostream_joiner
。
插入格式化输出,<boost/io/ostream_put.hpp>
描述
头文件 <boost/io/ostream_put.hpp>
提供了函数模板 boost::io::ostream_put
,用于满足 [ostream.formatted.reqmts] 要求的格式化输出。
示例
模板类 basic_string_view
的插入器可以实现如下:
template<class charT, class traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
const basic_string_view<charT, traits>& str)
{
return boost::io::ostream_put(os, str.data(), str.size());
}
参考
头文件概要
namespace boost {
namespace io {
template<class charT, class traits>
std::basic_ostream<charT, traits>&
ostream_put(std::basic_ostream<charT, traits>& os,
const charT* data, std::size_t size);
} // io
} // boost
自由函数
template<class charT, class traits>
std::basic_ostream<charT, traits>&
ostream_put(std::basic_ostream<charT, traits>& os,
const charT* data, std::size_t size);
- 效果
-
对
os
的行为类似于格式化插入器(如 [ostream.formatted.reqmts] 中所述)。创建一个大小为 characters,从data
开始的字符序列seq
,每个字符都使用os.widen()
([basic.ios.members])进行拓宽。确定seq
的填充,如 [ostream.formatted.reqmts] 中所述。将seq
插入os
。调用width(0)
。 - 返回
-
os
.
致谢
Glen Fernandes 更新了 basic_string_ref
和 basic_string_view
流插入运算符的实现,使其直接写入 basic_streambuf
,并将该功能重构为这个通用实用程序。
空流,<boost/io/nullstream.hpp>
描述
头文件 <boost/io/nullstream.hpp>
提供了类模板 boost::io::basic_nullbuf
,它是一个空流缓冲区,以及类模板 boost::io::basic_onullstream
,它是一个空流。
示例
以下程序将一个空流传递给一个函数。
#include <boost/io/ostream_joiner.hpp>
#include <fstream>
void setup(std::ostream& log);
int main(int argc, char* argv[])
{
if (argc == 2) {
std::ofstream file(argv[1]);
setup(file);
} else {
boost::io::onullstream none;
setup(none);
}
}
参考
头文件概要
namespace boost {
namespace io {
template<class CharT, class Traits = std::char_traits<Char> >
class basic_nullbuf
: public std::basic_streambuf<CharT, Traits> {
protected:
typename Traits::int_type overflow(typename Traits::int_type c) override;
std::streamsize xsputn(const CharT*, std::streamsize n) override;
};
template<class CharT, class Traits = std::char_traits<CharT> >
class basic_onullstream
: public std::basic_ostream<CharT, Traits> {
public:
basic_onullstream();
};
typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;
} // io
} // boost
致谢
Glen Fernandes 实现的 basic_nullbuf
和 basic_onullstream
。
版权和许可
-
版权所有 2002 Daryle Walker
-
版权所有 2002, 2006, 2007, 2009, 2010 Beman Dawes
-
版权所有 2019 Glen Joseph Fernandes