7

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?

5 Answers 5

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I like this - much simpler than messing around with function output iterators.
3
#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;
}

1 Comment

+1 This is a very nice solution! I would never have thought to use std::accumulate here. While this doesn't exactly answer my question, it solves my problem in a very neat way.
1

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;
  }
};

Comments

1

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.

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.