I'd like to convert a vector<char> to a std::string and do a conversion one the way.
I'm almost there, but the result of the code below is a vector<string>, while I'd like to have one string (a concatenation of all the string parts in the vector).
See my code example for details.
string ConvertHexToAscii(const char input)
{
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(input);
return oss.str();
}
vector<char> readBuffer; // this is my input
readBuffer.push_back(0x1c);
readBuffer.push_back(0x09);
vector<string> resultVec;
std::transform(readBuffer.begin(), readBuffer.end()
, back_inserter(resultVec)
, ConvertHexToAscii);
// resultVec[0] = "1C";
// resultVec[1] = "09";
The result I need is a string containing "1C09". How to achieve that with std::transform?
You were almost there; this works:
std::stringstream sstr;
std::transform(
input.begin(), input.end(),
std::ostream_iterator<std::string>(sstr, ""),
ConvertHexToAscii);
But unfortunately this instantiates quite a lot of string streams, which is inefficient. Ideally, the ConvertHexToAscii (misnamed, by the way! C++ doesn’t know about encodings) function would directly use the underlying stream.
#include <iostream>
#include <vector>
#include <iomanip>
#include <sstream>
#include <numeric>
std::string ConvertHexToAscii(std::string acc, char input)
{
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(input);
return acc + oss.str();
}
int main() {
std::vector<char> readBuffer; // this is my input
readBuffer.push_back(0x1c);
readBuffer.push_back(0x09);
std::cout << std::accumulate(readBuffer.begin(), readBuffer.end()
, std::string(), ConvertHexToAscii) << std::endl;
return 0;
}
create your own back_insert_iterator (look at the code in your stl lib, it's fairly simple) for string types of which operator = is defined as
template< class string_type, class value_type >
class back_insert_iterator
{
public:
back_insert_iterator< _string_type >& operator = ( const value_type& val )
{
container->append( val )
return *this;
}
};
You can do this with a function output iterator:
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <boost/function_output_iterator.hpp>
std::string ConvertHexToAscii(const char input)
{
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(input);
return oss.str();
}
int main() {
std::vector<char> readBuffer; // this is my input
readBuffer.push_back(0x1c);
readBuffer.push_back(0x09);
std::string temp;
std::transform(readBuffer.begin(), readBuffer.end()
, boost::make_function_output_iterator([&temp](const std::string& r) {temp.append(r);})
, ConvertHexToAscii);
std::cout << temp << std::endl;
}
I used a lambda to call the append() function on the result string, but if you don't have that available it's fairly easy to use boost::bind or just write an old fashioned functor to do that for you.
With boost::bind the function output iterator gets created as:
boost::make_function_output_iterator(boost::bind(static_cast<std::string& (std::string::*)(const std::string&)>(&std::string::append), &temp, _1))
instead. It's slightly clunky because you need to pick the right overload for std::string::append.
Whereas perreal's idea of using an accumulation isn't that bad, it may be more performant to operate on the stream directly instead of creating so many temporary strings (though move semantics may help with that):
std::ostringstream os;
std::string temp = std::accumulate(
readBuffer.begin(), readBuffer.end(), std::ref(os),
[](std::ostream &os, const char input)
-> std::reference_wrapper<std::ostream> {
return os << std::hex << std::setw(2)
<< std::setfill('0')
<< static_cast<int>(input);
}).str();
EDIT: But Ok, that's maybe a bit oversophistication here, a simple foreach would have done, too (even if not as semantically clean as an accumulation):
std::ostringstream os;
std::for_each(readBuffer.begin(), readBuffer.end(),
[&os](const char input) mutable {
os << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(input);
});
std::string temp = os.str();
But anything might be better than creating a whole bunch of temporary strings.
Related
The next code returns an empty string in ss:
#include <string>
#include <iostream>
#include <sstream>
int main () {
std::ostringstream oss;
oss << "Text";
std::stringstream ss;
ss.basic_ios<char>::rdbuf(oss.rdbuf());
std::cout << ss.str() << "\n";
return 0;
}
How can I return from ss the text introduced in oss? I'm mainly interested in converting std::ostringstream into std::stringstream.
You could make use of the protected std::streambuf::swap member function that exchanges the contents of the stream buffer with those of another
To get access to it, you'll need a derived class:
#include <iostream>
#include <sstream>
struct swapper : std::streambuf {
using std::streambuf::streambuf;
void swap(std::streambuf& rhs) { // public proxy for protected swap
std::streambuf::swap(rhs);
}
};
// casting
void swapbuf(std::ostream& a, std::ostream& b) {
static_cast<swapper*>(a.rdbuf())->swap(*b.rdbuf());
}
int main () {
std::ostringstream oss;
oss << "Text";
std::stringstream ss;
swapbuf(oss, ss);
std::cout << "ss: " << ss.str() << "\n"; // prints Text
}
Following comments from #NathanOliver, I decided to convert std::ostringstream into std::stringstream by using str():
#include <string>
#include <iostream>
#include <sstream>
int main () {
std::ostringstream oss;
oss << "Text";
std::stringstream ss;
ss << oss.str();
std::cout << ss.str() << "\n";
return 0;
}
I am still trying to wrap my head around Boost::Spirit.
I want to parse two words into a variable. When I can do that, into a struct.
The single word compiles, the Variable doesn't. Why?
#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
/*
class Syntax : public qi::parser{
};
*/
int main()
{
//get user input
std::string input;
std::getline(std::cin, input);
auto it = input.begin();
bool result;
//define grammar for a single word
auto word_grammar = +qi::alnum - qi::space;
std::string singleWord;
result = qi::parse(
it, input.end(),
word_grammar,
singleWord
);
if(!result){
std::cout << "Failed to parse a word" << '\n';
return -1;
}
std::cout << "\"" << singleWord << "\"" << '\n';
//Now parse two words into a variable
std::cout << "Variable:\n";
typedef boost::tuple<std::string, std::string> Variable;
Variable variable;
auto variable_grammar = word_grammar >> word_grammar;
result = qi::parse(
it, input.end(),
variable_grammar,
variable
);
if(!result){
std::cout << "Failed to parse a variable" << '\n';
return -1;
}
std::cout << "\"" << variable.get<0>() << "\" \"" << variable.get<1>() << "\"" << '\n';
//now parse a list of variables
std::cout << "List of Variables:\n";
std::list<Variable> variables;
result = qi::parse(
it, input.end(),
variable_grammar % +qi::space,
variable
);
if(!result){
std::cout << "Failed to parse a list of variables" << '\n';
return -1;
}
for(auto var : variables)
std::cout << "DataType: " << var.get<0>() << ", VariableName: " << var.get<1>() << '\n';
}
In the end I want to parse something like this:
int a
float b
string name
Templates are nice, but when problems occur the error messages are just not human readable (thus no point in posting them here).
I am using the gcc
Sorry to take so long. I've been building a new web server in a hurry and had much to learn.
Here is what it looks like in X3. I think it is easier to deal with than qi. And then, I've used it a lot more. But then qi is much more mature, richer. That said, x3 is meant to be adaptable, hackable. So you can make it do just about anything you want.
So, live on coliru
#include <string>
#include <iostream>
#include <vector>
#include <boost/spirit/home/x3.hpp>
#include <boost/tuple/tuple.hpp>
//as pointed out, for the error 'The parser expects tuple-like attribute type'
#include <boost/fusion/adapted/boost_tuple.hpp>
//our declarations
using Variable = boost::tuple<std::string, std::string>;
using Vector = std::vector<Variable>;
namespace parsers {
using namespace boost::spirit::x3;
auto const word = lexeme[+char_("a-zA-Z")];
//note, using 'space' as the stock skipper
auto const tuple = word >> word;
}
std::ostream& operator << (std::ostream& os, /*const*/ Variable& obj) {
return os << obj.get<0>() << ' ' << obj.get<1>();
}
std::ostream& operator << (std::ostream& os, /*const*/ Vector& obj) {
for (auto& item : obj)
os << item << " : ";
return os;
}
template<typename P, typename A>
bool test_parse(std::string in, P parser, A& attr) {
auto begin(in.begin());
bool r = phrase_parse(begin, in.end(), parser, boost::spirit::x3::space, attr);
std::cout << "result:\n " << attr << std::endl;
return r;
}
int main()
{
//not recomended but this is testing stuff
using namespace boost::spirit::x3;
using namespace parsers;
std::string input("first second third forth");
//parse one word
std::string singleWord;
test_parse(input, word, singleWord);
//parse two words into a variable
Variable variable;
test_parse(input, tuple, variable);
//parse two sets of two words
Vector vector;
test_parse(input, *tuple, vector);
}
You may like this form of testing. You can concentrate on testing parsers without a lot of extra code. It makes it easier down the road to keep your basic parsers in their own namespace. Oh yea, x3 compiles much faster than qi!
The single word compiles, the Variable doesn't. Why?
There are missing two #includes:
#include <boost/fusion/adapted/boost_tuple.hpp>
#include <boost/spirit/include/qi_list.hpp>
I am trying to move the map array but for this I need to put everything in buffer, here is my code.
#include <iostream>
#include <map>
#include <string>
std::map<std::int, std::string> maphack;
maphack.emplace(1, "CS");
maphack.emplace(2, "PBG");
maphack.emplace(3, "Maicraft");
maphack.insert_or_assign(3, "GTA5");
for (auto &a : maphack) {
std::cout << a.first << " : " << a.second << '\n';
}
How to put everything above in the buffer?
char buffer[64];
send(sock, buffer, AmountToSend, 0);
The simple answer is to use std::ostringstream and std::istringstream which allows to use std streams machinery with a string storage. Which can be converted to char* with c_str() method if needed later, full example is below.
The better answer can be to use more heavy lifting library for serialization, the choice of the format will depend on your program. Do you need binary or text format? Do you want other programs (in other programming languages) be able to read it? Do you need to support different endian system? Do you need to handle error when deserializing malformed input? Boost, Qt, google protobuf, and other ones are out there.
#include <iostream>
#include <map>
#include <sstream>
#include <string>
void write_map(const std::map<int, std::string> &m, std::ostream &out) {
for (auto &a : m) {
out << a.first << " " << a.second << '\n';
}
}
std::map<int, std::string> read_map(std::istream &in) {
std::map<int, std::string> m;
int i;
std::string s;
while (in >> i >> s) {
m.emplace(i, s);
}
return m;
}
int main() {
std::map<int, std::string> m;
m.emplace(1, "CS");
m.emplace(2, "");
m.emplace(3, "Maicraft");
m.insert_or_assign(3, "GTA5");
std::ostringstream out;
write_map(m, out);
std::string data = out.str();
std::cout << "Data:\n" << data << std::endl;
// send data over socket
// ...
std::istringstream in(data);
auto m1 = read_map(in);
std::cout << "Read:\n";
write_map(m1, std::cout);
std::cout << std::endl;
}
In c++ we have std::to_string which converts int/float/double to strings. So just to test my understanding of templates I tried the code below:
#include "iostream"
#include "sstream"
#include "string"
using std::cout;
template <typename T>
std::string getString(const T& data){
std::stringstream ss;
cout << '\n' << data << '\n';
ss << data;
std::string s;
ss >> s;
return s;
}
int main(int argc , char** argv){
cout << getString(1.0000011);
cout <<' '<<std::to_string(1.0000011);
return 0;
}
However, the output doesn't make sense, to_string gives me 1.0000011, whereas getString gets 1 and gives me 1. As I am using templates shouldn't getString get 1.0000011 as well and give me 1.0000011 too?
You can use std::setprecision in the <iomanip> header to set the precision that std::stringstream will use when formatting numeric data.
For example:
std::stringstream ss;
ss << std::setprecision(9) << data;
cout << ss.str();
Will print:
1.0000011
Here's a quick demo online: cpp.sh/9v7xf
As a side note, you don't have to create a string and output from the stringstream - you can replace the last 3 lines in getString() with:
return ss.str();
Numeric values are often truncated for appearance. You can supply the std::fixed manipulator from the iomanip standard header to avoid this issue.
#include "iomanip" // <- Add this header
#include "iostream"
#include "sstream"
#include "string"
using std::cout;
template <typename T>
std::string getString(const T& data)
{
std::stringstream ss;
cout << '\n' << data << '\n';
ss << std::fixed << data;
// ^^^^^^^^^^^^^ Add this
std::string s;
ss >> s;
return s;
}
int main(int argc, char** argv)
{
cout << getString(1.0000011);
cout << ' ' << std::to_string(1.0000011);
return 0;
}
<iomanip> needs included, and std::setprecision must be used when outputting float values to streams. Using your example, this looks like:
#include <iostream>
#include <iomanip> //include this.
#include <sstream>
#include <string>
template <typename T>
std::string getString(const T& data){
std::ostringstream ss;
ss << std::setprecision(8);
std::cout << std::setprecision(8);
std::cout << data << '\n';
ss << data;
return ss.str();
}
int main(int argc , char** argv){
std::cout << getString(1.0000011) << "\n";
std::cout << std::to_string(1.0000011) << std::endl;
return 0;
}
Which prints:
1.0000011
1.0000011
1.000001
Program ended with exit code: 0
Note how to_string alone truncates the floating point number!!! I suspect this is undesired behavior, but to_string cannot be manipulated directly, so...
If desired, you can fix this with the solution found here.
Otherwise, just use std:set_precision() when inserting into streams for precisely converted strings.
I should know this already but... printf is to sprintf as cout is to ____? Please give an example.
It sounds like you are looking for std::ostringstream.
Of course C++ streams don't use format-specifiers like C's printf()-type functions; they use manipulators.
Example, as requested:
#include <sstream>
#include <iomanip>
#include <cassert>
std::string stringify(double x, size_t precision)
{
std::ostringstream o;
o << std::fixed << std::setprecision(precision) << x;
return o.str();
}
int main()
{
assert(stringify(42.0, 6) == "42.000000");
return 0;
}
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
ostringstream s;
s.precision(3);
s << "pi = " << fixed << 3.141592;
cout << s.str() << endl;
return 0;
}
Output:
pi = 3.142
Here's an example:
#include <sstream>
int main()
{
std::stringstream sout;
sout << "Hello " << 10 << "\n";
const std::string s = sout.str();
std::cout << s;
return 0;
}
If you want to clear the stream for reuse, you can do
sout.str(std::string());
Also look at the Boost Format library.
std::ostringstream
You can use this to create something like the Boost lexical cast:
#include <sstream>
#include <string>
template <typename T>
std::string ToString( const T & t ) {
std::ostringstream os;
os << t;
return os.str();
}
In use:
string is = ToString( 42 ); // is contains "42"
string fs = ToString( 1.23 ) ; // fs contains something approximating "1.23"
You have a little misunderstanding for the concept of cout. cout is a stream and the operator << is defined for any stream. So, you just need another stream that writes to string in order to output your data. You can use a standard stream like std::ostringstream or define your own one.
So your analogy is not very precise, since cout is not a function like printf and sprintf