mirror of
https://github.com/boostorg/io.git
synced 2025-07-30 20:37:12 +02:00
Add boost/io/detail/quoted_manip.hpp, with docs and test
[SVN r63119]
This commit is contained in:
162
doc/quoted_manip.html
Normal file
162
doc/quoted_manip.html
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||||
|
<title>Boost "quoted" I/O manipulator</title>
|
||||||
|
<meta name="generator" content="Microsoft FrontPage 5.0" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../../doc/html/minimal.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="5" cellspacing="0"
|
||||||
|
style="border-collapse: collapse">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width="277"><a href="../../../index.htm"><img
|
||||||
|
src="../../../boost.png" alt="boost.png (6897 bytes)" align="middle"
|
||||||
|
width="300" height="86" border="0" /></a></td>
|
||||||
|
<td>
|
||||||
|
<h1 align="center">"Quoted"
|
||||||
|
I/O Manipulators<br>
|
||||||
|
for Strings</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table border="1" cellpadding="5" cellspacing="1" style="border-collapse: collapse" bordercolor="#111111">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p align="center"><b>"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>, and
|
||||||
|
this documentation page is not linked to from official documentation.</b></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
<p>C++ Standard library stream I/O for strings that contain embedded spaces
|
||||||
|
can produce unexpected results. For example,</p>
|
||||||
|
<blockquote>
|
||||||
|
<pre>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</pre>
|
||||||
|
</blockquote>
|
||||||
|
<p>The Boost <code>quoted</code> stream I/O manipulator places delimiters, defaulted
|
||||||
|
to the double-quote (<code>"</code>), around strings on output, and strips off
|
||||||
|
the delimiters on input. This ensures strings with embedded spaces round-trip as
|
||||||
|
desired. For example,</p>
|
||||||
|
<blockquote>
|
||||||
|
<pre>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</pre>
|
||||||
|
</blockquote>
|
||||||
|
<p>If the string contains the delimiter character, on output that character will
|
||||||
|
be preceded by an escape character, as will the escape character itself:</p>
|
||||||
|
<blockquote>
|
||||||
|
<pre>std::cout << quoted("'Jack & Jill'", '&', '\''); // outputs: '&'Jack && Jill&''</pre>
|
||||||
|
</blockquote>
|
||||||
|
<h2>Header <a href="../../../boost/io/detail/quoted_manip.hpp"><boost/io/quoted_manip.hpp></a> synopsis</h2>
|
||||||
|
<pre>namespace boost
|
||||||
|
{
|
||||||
|
namespace io
|
||||||
|
{
|
||||||
|
// manipulator for const std::basic_string&
|
||||||
|
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
<b><i>unspecified-type1</i></b> quoted(const std::basic_string<Char, Traits, Alloc>& string, Char escape='\\', Char delim='\"');
|
||||||
|
|
||||||
|
// manipulator for const C-string*
|
||||||
|
|
||||||
|
template <class Char>
|
||||||
|
<b><i>unspecified-type2</i></b> quoted(const Char* string, Char escape='\\', Char delim='\"');
|
||||||
|
|
||||||
|
// manipulator for non-const std::basic_string&
|
||||||
|
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
<b><i>unspecified-type3</i></b> quoted(std::basic_string<Char, Traits, Alloc>& string, Char escape='\\', Char delim='\"');
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
<p><i><b><code>unspecified_type1</code></b></i>, <i><b><code>unspecified_type2</code></b></i>,
|
||||||
|
and <i><b><code>unspecified_type3</code></b></i> are implementation supplied
|
||||||
|
types with implementation supplied <code>operator<<</code>:</p>
|
||||||
|
<blockquote>
|
||||||
|
<pre>template <class Char, class Traits>
|
||||||
|
std::basic_ostream<Char, Traits>&
|
||||||
|
operator<<(std::basic_ostream<Char, Traits>& os, const <i><b><code>unspecified_typeN</code></b></i>& proxy);</pre>
|
||||||
|
<p><i>Effects:</i> Inserts characters into <code>os</code>:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>delim</code>.</li>
|
||||||
|
<li>Each character in <code>string</code>. If the character to be output is
|
||||||
|
equal to <code>escape</code> or <code>delim</code>, as determined by <code>
|
||||||
|
operator==</code>, first output <code>escape</code>. </li>
|
||||||
|
<li><code>delim</code>.</li>
|
||||||
|
</ul>
|
||||||
|
<p><i>Remarks:</i> <code>string</code>, <code>escape</code>, and <code>delim</code>
|
||||||
|
have the type and value of the corresponding arguments of the call to the <code>
|
||||||
|
quoted</code> function that constructed <code>proxy</code>.</p>
|
||||||
|
<p><i>Returns:</i> <code>os</code>. </p>
|
||||||
|
</blockquote>
|
||||||
|
<p><i><b><code>unspecified_type3</code></b></i> is an implementation supplied
|
||||||
|
type with an implementation supplied <code>operator>></code>:</p>
|
||||||
|
<blockquote>
|
||||||
|
<pre>template <class Char, class Traits>
|
||||||
|
std::basic_istream<Char, Traits>&
|
||||||
|
operator>>(std::basic_istream<Char, Traits>& is, const <i><b><code>unspecified_type3</code></b></i>& proxy);</pre>
|
||||||
|
<p><i>Effects:</i> Extracts characters from <code>os</code>:</p>
|
||||||
|
<ul>
|
||||||
|
<li>If the first character extracted is equal to delim, as determined by
|
||||||
|
<code>operator==</code>, then:<ul>
|
||||||
|
<li>Turn off the <code>skipws</code> flag.</li>
|
||||||
|
<li><code>string.clear()</code></li>
|
||||||
|
<li>Until an unescaped <code>delim</code> character is reached or <code>
|
||||||
|
is.not_good()</code>, extract
|
||||||
|
characters from <code>os</code> and append them to <code>string</code>,
|
||||||
|
except that if an <code>escape</code> is reached, ignore it and append the
|
||||||
|
next character to <code>string</code>.</li>
|
||||||
|
<li>Discard the final <code>delim</code> character.</li>
|
||||||
|
<li>Restore the <code>skipws</code> flag to its original value.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Otherwise, <code>os >> string</code>.</li>
|
||||||
|
</ul>
|
||||||
|
<p><i>Remarks:</i> <code>string</code>, <code>escape</code>, and <code>delim</code>
|
||||||
|
have the type and value of the corresponding arguments of the call to the <code>
|
||||||
|
quoted</code> function that constructed <code>proxy</code>.</p>
|
||||||
|
<p><i>Returns:</i> <code>is</code>. </p>
|
||||||
|
</blockquote>
|
||||||
|
<h2>Acknowledgements</h2>
|
||||||
|
<p>The <code>quoted()</code> 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. </p>
|
||||||
|
<hr>
|
||||||
|
<p><EFBFBD> Copyright Beman Dawes, 2002, 2006, 2007, 2009, 2010</p>
|
||||||
|
<p>Distributed under the Boost Software License, Version 1.0. See
|
||||||
|
<a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a></p>
|
||||||
|
<p>Revised
|
||||||
|
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B %Y" startspan -->19 June 2010<!--webbot bot="Timestamp" endspan i-checksum="17561" --></p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
187
include/boost/io/detail/quoted_manip.hpp
Normal file
187
include/boost/io/detail/quoted_manip.hpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// boost/io/quoted_manip.hpp ---------------------------------------------------------//
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
#ifndef BOOST_IO_QUOTED_MANIP
|
||||||
|
#define BOOST_IO_QUOTED_MANIP
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <ios>
|
||||||
|
#include <string>
|
||||||
|
#include <iterator>
|
||||||
|
#include <boost/io/ios_state.hpp>
|
||||||
|
|
||||||
|
namespace boost
|
||||||
|
{
|
||||||
|
namespace io
|
||||||
|
{
|
||||||
|
namespace detail { template <class String, class Char> struct quoted_proxy; }
|
||||||
|
|
||||||
|
// ------------ public interface ------------------------------------------------//
|
||||||
|
|
||||||
|
// manipulator for const std::basic_string&
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
detail::quoted_proxy<std::basic_string<Char, Traits, Alloc> const &, Char>
|
||||||
|
quoted(const std::basic_string<Char, Traits, Alloc>& s,
|
||||||
|
Char escape='\\', Char delim='\"');
|
||||||
|
|
||||||
|
// manipulator for non-const std::basic_string&
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
detail::quoted_proxy<std::basic_string<Char, Traits, Alloc> &, Char>
|
||||||
|
quoted(std::basic_string<Char, Traits, Alloc>& s,
|
||||||
|
Char escape='\\', Char delim='\"');
|
||||||
|
|
||||||
|
// manipulator for const C-string*
|
||||||
|
template <class Char>
|
||||||
|
detail::quoted_proxy<const Char*, Char>
|
||||||
|
quoted(const Char* s, Char escape='\\', Char delim='\"');
|
||||||
|
|
||||||
|
// ----------- implementation details -------------------------------------------//
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// proxy used as an argument pack
|
||||||
|
template <class String, class Char>
|
||||||
|
struct quoted_proxy
|
||||||
|
{
|
||||||
|
String string;
|
||||||
|
Char escape;
|
||||||
|
Char delim;
|
||||||
|
|
||||||
|
quoted_proxy(String s_, Char escape_, Char delim_)
|
||||||
|
: string(s_), escape(escape_), delim(delim_) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// abstract away difference between proxies with const or non-const basic_strings
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
std::basic_ostream<Char, Traits>&
|
||||||
|
basic_string_inserter_imp(std::basic_ostream<Char, Traits>& os,
|
||||||
|
std::basic_string<Char, Traits, Alloc> const & string, Char escape, Char delim)
|
||||||
|
{
|
||||||
|
os << delim;
|
||||||
|
typename std::basic_string<Char, Traits, Alloc>::const_iterator
|
||||||
|
end_it = string.end();
|
||||||
|
for (typename std::basic_string<Char, Traits, Alloc>::const_iterator
|
||||||
|
it = string.begin();
|
||||||
|
it != end_it;
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
if (*it == delim || *it == escape)
|
||||||
|
os << escape;
|
||||||
|
os << *it;
|
||||||
|
}
|
||||||
|
os << delim;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inserter for const std::basic_string& proxies
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
inline
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os,
|
||||||
|
const quoted_proxy<std::basic_string<Char, Traits, Alloc> const &, Char>& proxy)
|
||||||
|
{
|
||||||
|
return basic_string_inserter_imp(os, proxy.string, proxy.escape, proxy.delim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// inserter for non-const std::basic_string& proxies
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
inline
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os,
|
||||||
|
const quoted_proxy<std::basic_string<Char, Traits, Alloc>&, Char>& proxy)
|
||||||
|
{
|
||||||
|
return basic_string_inserter_imp(os, proxy.string, proxy.escape, proxy.delim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// inserter for const C-string* proxies
|
||||||
|
template <class Char, class Traits>
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os,
|
||||||
|
const quoted_proxy<const Char*, Char>& proxy)
|
||||||
|
{
|
||||||
|
os << proxy.delim;
|
||||||
|
for (const Char* it = proxy.string;
|
||||||
|
*it;
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
if (*it == proxy.delim || *it == proxy.escape)
|
||||||
|
os << proxy.escape;
|
||||||
|
os << *it;
|
||||||
|
}
|
||||||
|
os << proxy.delim;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractor for non-const std::basic_string& proxies
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is,
|
||||||
|
const quoted_proxy<std::basic_string<Char, Traits, Alloc>&, Char>& proxy)
|
||||||
|
{
|
||||||
|
Char c;
|
||||||
|
is >> c;
|
||||||
|
if (c != proxy.delim)
|
||||||
|
{
|
||||||
|
proxy.string = c;
|
||||||
|
is >> proxy.string;
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
proxy.string.clear();
|
||||||
|
{
|
||||||
|
boost::io::ios_flags_saver ifs(is);
|
||||||
|
is >> std::noskipws;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
is >> c;
|
||||||
|
if (!is.good()) // cope with I/O errors or end-of-file
|
||||||
|
break;
|
||||||
|
if (c == proxy.escape)
|
||||||
|
{
|
||||||
|
is >> c;
|
||||||
|
if (!is.good()) // cope with I/O errors or end-of-file
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (c == proxy.delim)
|
||||||
|
break;
|
||||||
|
proxy.string += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
// manipulator implementation for const std::basic_string&
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
inline detail::quoted_proxy<std::basic_string<Char, Traits, Alloc> const &, Char>
|
||||||
|
quoted(const std::basic_string<Char, Traits, Alloc>& s, Char escape, Char delim)
|
||||||
|
{
|
||||||
|
return detail::quoted_proxy<std::basic_string<Char, Traits, Alloc> const &, Char>
|
||||||
|
(s, escape, delim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// manipulator implementation for non-const std::basic_string&
|
||||||
|
template <class Char, class Traits, class Alloc>
|
||||||
|
inline detail::quoted_proxy<std::basic_string<Char, Traits, Alloc> &, Char>
|
||||||
|
quoted(std::basic_string<Char, Traits, Alloc>& s, Char escape, Char delim)
|
||||||
|
{
|
||||||
|
return detail::quoted_proxy<std::basic_string<Char, Traits, Alloc>&, Char>
|
||||||
|
(s, escape, delim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// manipulator implementation for const C-string*
|
||||||
|
template <class Char>
|
||||||
|
inline detail::quoted_proxy<const Char*, Char>
|
||||||
|
quoted(const Char* s, Char escape, Char delim)
|
||||||
|
{
|
||||||
|
return detail::quoted_proxy<const Char*, Char> (s, escape, delim);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace io
|
||||||
|
} // namespace boost
|
||||||
|
|
||||||
|
#endif // BOOST_IO_QUOTED_MANIP
|
@ -21,4 +21,6 @@ test-suite "io"
|
|||||||
: # input files
|
: # input files
|
||||||
# : std::locale-support
|
# : std::locale-support
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[ run quoted_manip_test.cpp ]
|
||||||
;
|
;
|
||||||
|
92
test/quoted_manip_test.cpp
Normal file
92
test/quoted_manip_test.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 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 <boost/io/detail/quoted_manip.hpp>
|
||||||
|
#include <boost/detail/lightweight_test.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using boost::io::quoted;
|
||||||
|
using std::string;
|
||||||
|
using std::wstring;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
std::wstringstream wss;
|
||||||
|
|
||||||
|
const string s1("foo\\bar, \" *");
|
||||||
|
string r; // test results
|
||||||
|
|
||||||
|
ss << quoted(s1);
|
||||||
|
ss >> r;
|
||||||
|
BOOST_TEST_EQ(r, "\"foo\\\\bar, \\\" *\"");
|
||||||
|
|
||||||
|
ss << quoted(s1.c_str());
|
||||||
|
ss >> r;
|
||||||
|
BOOST_TEST_EQ(r, "\"foo\\\\bar, \\\" *\"");
|
||||||
|
|
||||||
|
ss << quoted(s1);
|
||||||
|
ss >> quoted(r);
|
||||||
|
BOOST_TEST_EQ(r, s1);
|
||||||
|
|
||||||
|
ss << quoted(s1.c_str());
|
||||||
|
ss >> quoted(r);
|
||||||
|
BOOST_TEST_EQ(r, s1);
|
||||||
|
|
||||||
|
string s2("'Jack & Jill'");
|
||||||
|
|
||||||
|
ss << quoted(s2, '&', '\'');
|
||||||
|
ss >> r;
|
||||||
|
BOOST_TEST_EQ(r, "'&'Jack && Jill&''");
|
||||||
|
|
||||||
|
ss << quoted(s2, '&', '\'');
|
||||||
|
ss >> quoted(r, '&', '\'');
|
||||||
|
BOOST_TEST_EQ(r, s2);
|
||||||
|
|
||||||
|
wstring ws1(L"foo$bar, \" *");
|
||||||
|
wstring wr; // test results
|
||||||
|
|
||||||
|
wss << quoted(ws1, L'$');
|
||||||
|
wss >> wr;
|
||||||
|
BOOST_TEST(wr == wstring(L"\"foo$$bar, $\" *\""));
|
||||||
|
|
||||||
|
wss << quoted(ws1, L'$');
|
||||||
|
wss >> quoted(wr, L'$');
|
||||||
|
BOOST_TEST(wr == ws1);
|
||||||
|
|
||||||
|
const string s3("const string");
|
||||||
|
ss << quoted(s3);
|
||||||
|
ss >> quoted(r);
|
||||||
|
BOOST_TEST_EQ(r, s3);
|
||||||
|
|
||||||
|
// missing end delimiter test
|
||||||
|
ss << "\"abc"; // load ss with faulty quoting
|
||||||
|
ss >> quoted(r); // this loops if istream error/eof not detected
|
||||||
|
BOOST_TEST_EQ(r, "abc");
|
||||||
|
|
||||||
|
// no initial delmiter test
|
||||||
|
ss << "abc";
|
||||||
|
ss >> quoted(r);
|
||||||
|
BOOST_TEST_EQ(r, "abc");
|
||||||
|
|
||||||
|
// no initial delmiter, space in ss
|
||||||
|
ss << "abc def";
|
||||||
|
ss >> quoted(r);
|
||||||
|
BOOST_TEST_EQ(r, "abc");
|
||||||
|
|
||||||
|
// these should fail to compile because the arguments are const:
|
||||||
|
// ss >> quoted(s1);
|
||||||
|
// ss >> quoted("foo");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user