diff --git a/doc/quoted_manip.html b/doc/quoted_manip.html new file mode 100644 index 0000000..c3e7362 --- /dev/null +++ b/doc/quoted_manip.html @@ -0,0 +1,161 @@ + + + + + + Boost "quoted" I/O manipulator + + + + + + + + + + + + + +
boost.png (6897 bytes) +

"Quoted" + I/O Manipulators
+ for Strings

+
+ + + + + +
+

"Quoted" + I/O Manipulators + for Strings are not yet accepted into Boost as public components. Thus the + header file is currently located in <boost/io/detail/quoted_manip.hpp>

+ +

Introduction

+

C++ Standard library stream I/O for strings that contain embedded spaces +can produce unexpected results. For example,

+
+
std::stringstream ss;
+std::string original = "fooled you";
+std::string round_trip;
+
+ss << original;
+ss >> round_trip;
+
+std::cout << original;   // outputs: fooled you
+std::cout << round_trip; // outputs: fooled
+
+assert(original == round_trip); // assert will fire
+
+

The Boost quoted stream I/O manipulator places delimiters, defaulted +to the double-quote ("), around strings on output, and strips off +the delimiters on input. This ensures strings with embedded spaces round-trip as +desired. For example,

+
+
std::stringstream ss;
+std::string original = "fooled you";
+std::string round_trip;
+
+ss << quoted(original);
+ss >> quoted(round_trip);
+
+std::cout << quoted(original); // outputs: "fooled you"
+std::cout << round_trip;       // outputs: fooled you
+
+assert(original == round_trip); // assert will not fire
+
+

If the string contains the delimiter character, on output that character will +be preceded by an escape character, as will the escape character itself:

+
+
std::cout << quoted("'Jack & Jill'", '&', '\'');  // outputs: '&'Jack && Jill&''
+
+

Header <boost/io/quoted_manip.hpp> synopsis

+
namespace boost
+{
+  namespace io
+  {
+    // manipulator for const std::basic_string&
+
+    template <class Char, class Traits, class Alloc>
+    unspecified-type1 quoted(const std::basic_string<Char, Traits, Alloc>& string, Char escape='\\', Char delim='\"');
+
+    // manipulator for const C-string*
+
+    template <class Char>
+    unspecified-type2 quoted(const Char* string, Char escape='\\', Char delim='\"');
+
+    // manipulator for non-const std::basic_string&
+
+    template <class Char, class Traits, class Alloc>
+    unspecified-type3 quoted(std::basic_string<Char, Traits, Alloc>& string, Char escape='\\', Char delim='\"');
+  }
+}
+

unspecified_type1, unspecified_type2, +and unspecified_type3 are implementation supplied +types with implementation supplied operator<<:

+
+
template <class Char, class Traits>
+  std::basic_ostream<Char, Traits>&
+    operator<<(std::basic_ostream<Char, Traits>& os, const unspecified_typeN& proxy);
+

Effects: Inserts characters into os:

+ +

Remarks: string, escape, and delim +have the type and value of the corresponding arguments of the call to the +quoted function that constructed proxy.

+

Returns: os.

+
+

unspecified_type3 is an implementation supplied +type with an implementation supplied operator>>:

+
+
template <class Char, class Traits>
+  std::basic_istream<Char, Traits>&
+    operator>>(std::basic_istream<Char, Traits>& is, const unspecified_type3& proxy);
+

Effects: Extracts characters from os:

+ +

Remarks: string, escape, and delim +have the type and value of the corresponding arguments of the call to the +quoted function that constructed proxy.

+

Returns: is.

+
+

Acknowledgements

+

The quoted() stream manipulator emerged from discussions on the +Boost developers mailing list. Participants included Beman Dawes, Rob Stewart, +Alexander Lamaison, Eric Niebler, Vicente Botet, Andrey Semashev, Phil Richards, +and Rob Murray. Eric Niebler's suggestions provided the basis for the name and +form of the templates.

+
+

© Copyright Beman Dawes, 2002, 2006, 2007, 2009, 2010

+

Distributed under the Boost Software License, Version 1.0. See +www.boost.org/LICENSE_1_0.txt

+

Revised +20 June 2010

+ + + \ No newline at end of file diff --git a/include/boost/io/detail/quoted_manip.hpp b/include/boost/io/detail/quoted_manip.hpp index 13cfc35..502f422 100644 --- a/include/boost/io/detail/quoted_manip.hpp +++ b/include/boost/io/detail/quoted_manip.hpp @@ -125,15 +125,15 @@ namespace boost std::basic_istream& operator>>(std::basic_istream& is, const quoted_proxy&, Char>& proxy) { + proxy.string.clear(); Char c; is >> c; if (c != proxy.delim) { - proxy.string = c; + is.unget(); is >> proxy.string; return is; } - proxy.string.clear(); { boost::io::ios_flags_saver ifs(is); is >> std::noskipws; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 14b3e5b..4b48bce 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -21,4 +21,6 @@ test-suite "io" : # input files # : std::locale-support ] + + [ run quoted_manip_test.cpp ] ; diff --git a/test/ios_state_test.cpp b/test/ios_state_test.cpp index 1f7fa84..0900de9 100644 --- a/test/ios_state_test.cpp +++ b/test/ios_state_test.cpp @@ -16,7 +16,7 @@ #include // for boost::io::ios_flags_saver, etc. #include // for std::size_t -#include // for std::setw +#include // for boost::detail::setw #include // for std::ios_base, std::streamsize, etc. #include // for std::cout, etc. #include // for std::istream @@ -143,7 +143,7 @@ saver_tests_1 { using std::locale; using std::ios_base; - using std::setw; + using boost::detail::setw; boost::io::ios_flags_saver const ifls( output ); boost::io::ios_precision_saver const iprs( output ); @@ -168,8 +168,8 @@ saver_tests_1 output.fill( '@' ); output.precision( 9 ); output << '\t' << test_string << '\n'; - output << '\t' << setw( 10 ) << test_num1 << '\n'; - output << '\t' << setw( 15 ) << test_num2 << '\n'; + output << '\t' << boost::detail::setw( 10 ) << test_num1 << '\n'; + output << '\t' << boost::detail::setw( 15 ) << test_num2 << '\n'; output.imbue( loc ); output << '\t' << test_bool << '\n'; diff --git a/test/ios_state_unit_test.cpp b/test/ios_state_unit_test.cpp index 72ce7b5..0d5969c 100644 --- a/test/ios_state_unit_test.cpp +++ b/test/ios_state_unit_test.cpp @@ -13,7 +13,7 @@ #include // for main, BOOST_CHECK, etc. #include // for NULL -#include // for std::setiosflags, etc. +#include // for boost::detail::setiosflags, etc. #include // for std::ios_base #include // for std::cout, std::cerr, etc. #include // for std::iostream @@ -77,7 +77,7 @@ ios_flags_saver_unit_test BOOST_CHECK_EQUAL( (ios_base::showbase | ios_base::internal), ss.flags() ); - ss << setiosflags( ios_base::unitbuf ); + ss << boost::detail::setiosflags( ios_base::unitbuf ); BOOST_CHECK_EQUAL( (ios_base::showbase | ios_base::internal | ios_base::unitbuf), ss.flags() ); } @@ -102,7 +102,7 @@ ios_precision_saver_unit_test BOOST_CHECK_EQUAL( 6, ss.precision() ); - ss << setprecision( 4 ); + ss << boost::detail::setprecision( 4 ); BOOST_CHECK_EQUAL( 4, ss.precision() ); } @@ -113,7 +113,7 @@ ios_precision_saver_unit_test BOOST_CHECK_EQUAL( 8, ss.precision() ); - ss << setprecision( 10 ); + ss << boost::detail::setprecision( 10 ); BOOST_CHECK_EQUAL( 10, ss.precision() ); } @@ -137,7 +137,7 @@ ios_width_saver_unit_test BOOST_CHECK_EQUAL( 0, ss.width() ); - ss << setw( 4 ); + ss << boost::detail::setw( 4 ); BOOST_CHECK_EQUAL( 4, ss.width() ); } @@ -148,7 +148,7 @@ ios_width_saver_unit_test BOOST_CHECK_EQUAL( 8, ss.width() ); - ss << setw( 10 ); + ss << boost::detail::setw( 10 ); BOOST_CHECK_EQUAL( 10, ss.width() ); } @@ -507,7 +507,7 @@ ios_base_all_saver_unit_test BOOST_CHECK_EQUAL( 6, ss.precision() ); BOOST_CHECK_EQUAL( 0, ss.width() ); - ss << hex << unitbuf << setprecision( 5 ) << setw( 7 ); + ss << hex << unitbuf << boost::detail::setprecision( 5 ) << boost::detail::setw( 7 ); BOOST_CHECK_EQUAL( (ios_base::unitbuf | ios_base::hex | ios_base::skipws), ss.flags() ); BOOST_CHECK_EQUAL( 5, ss.precision() ); @@ -560,10 +560,10 @@ ios_all_saver_unit_test ss << oct << showpos << noskipws; BOOST_CHECK_EQUAL( (ios_base::showpos | ios_base::oct), ss.flags() ); - ss << setprecision( 3 ); + ss << boost::detail::setprecision( 3 ); BOOST_CHECK_EQUAL( 3, ss.precision() ); - ss << setw( 9 ); + ss << boost::detail::setw( 9 ); BOOST_CHECK_EQUAL( 9, ss.width() ); ss.setstate( ios_base::eofbit ); @@ -586,7 +586,7 @@ ios_all_saver_unit_test ss.rdbuf( cerr.rdbuf() ); BOOST_CHECK_EQUAL( cerr.rdbuf(), ss.rdbuf() ); - ss << setfill( 'x' ); + ss << boost::detail::setfill( 'x' ); BOOST_CHECK_EQUAL( 'x', ss.fill() ); ss.imbue( locale(locale::classic(), new backward_bool_names) ); diff --git a/test/quoted_manip_test.cpp b/test/quoted_manip_test.cpp new file mode 100644 index 0000000..31c6f05 --- /dev/null +++ b/test/quoted_manip_test.cpp @@ -0,0 +1,133 @@ +// libs/io/test/quote_manip_test.cpp ----------------------------------------------- // + +// Copyright Beman Dawes 2010 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +// Library home page: http://www.boost.org/libs/io + +// ---------------------------------------------------------------------------------- // + +#include +#include +#include +#include + +using boost::io::quoted; +using std::string; +using std::wstring; + +int main() +{ + + std::wstringstream wss; + + string r; // test results + + const string s0("foo"); + { + std::stringstream ss; + ss << quoted(s0); + ss >> r; + BOOST_TEST(r == "\"foo\""); + } + { + std::stringstream ss; + ss << quoted(s0); + ss >> quoted(r); + BOOST_TEST(r == "foo"); + } + + const string s0s("foo bar"); + { + std::stringstream ss; + ss << quoted(s0s); + ss >> r; + BOOST_TEST(r == "\"foo"); + } + { + std::stringstream ss; + ss << quoted(s0s); + ss >> quoted(r); + BOOST_TEST(r == "foo bar"); + } + + const string s1("foo\\bar, \" *"); + { + std::stringstream ss; + ss << quoted(s1); + ss >> r; + BOOST_TEST(r == "\"foo\\\\bar,"); + } + { + std::stringstream ss; + ss << quoted("foo\\bar, \" *"); + ss >> r; + BOOST_TEST(r == "\"foo\\\\bar,"); + } + { + std::stringstream ss; + ss << quoted(s1); + ss >> quoted(r); + BOOST_TEST(r == s1); + } + { + std::stringstream ss; + ss << quoted(s1.c_str()); + ss >> quoted(r); + BOOST_TEST(r == s1); + } + + string s2("'Jack & Jill'"); + { + std::stringstream ss; + ss << quoted(s2, '&', '\''); + ss >> quoted(r, '&', '\''); + BOOST_TEST(r == s2); + } + + wstring ws1(L"foo$bar, \" *"); + wstring wr; // test results + { + std::wstringstream wss; + wss << quoted(ws1, L'$'); + wss >> quoted(wr, L'$'); + BOOST_TEST(wr == ws1); + } + + const string s3("const string"); + { + std::stringstream ss; + ss << quoted(s3); + ss >> quoted(r); + BOOST_TEST(r == s3); + } + { + // missing end delimiter test + std::stringstream ss; + ss << "\"abc"; // load ss with faulty quoting + ss >> quoted(r); // this loops if istream error/eof not detected + BOOST_TEST(r == "abc"); + } + { + // no initial delmiter test + std::stringstream ss; + ss << "abc"; + ss >> quoted(r); + BOOST_TEST(r == "abc"); + } + { + // no initial delmiter, space in ss + std::stringstream ss; + ss << "abc def"; + ss >> quoted(r); + BOOST_TEST(r == "abc"); + } + + // these should fail to compile because the arguments are const: + // ss >> quoted(s1); + // ss >> quoted("foo"); + + return boost::report_errors(); +}