Better way to convert a vector of uint8 to an asci

2019-06-03 06:08发布

问题:

I coded the following function to convert a std::vector of uint8_t to an ascii hexadecimal string (gnu++98 standard).

 ...
string uint8_vector_to_hex_string(const vector<uint8_t>& v) {
    stringstream ss;
    vector<uint8_t>::const_iterator it;

    for (it = v.begin(); it != v.end(); it++) {
        char hex_char[2];
        sprintf(hex_char, "%x", *it);
        ss << "\\x" << hex_char;
    }

    return ss.str();
}
 ...

It works fine. I was wondering if there is a better way to do this transformation, maybe using not both the stringstream object and the sprintf function. Any suggestion?

回答1:

You could use the stringstream directly to do hex formatting:

...
string uint8_vector_to_hex_string(const vector<uint8_t>& v) {
    stringstream ss;
    ss << std::hex << std::setfill('0');
    vector<uint8_t>::const_iterator it;

    for (it = v.begin(); it != v.end(); it++) {
        ss << "\\x" << std::setw(2) << static_cast<unsigned>(*it);
    }

    return ss.str();
}
...


回答2:

(Note: The answer is for standard C++; minor modifications regarding range iteration may be needed to work in older dialects. I consider those immaterial to the core problem of algorithmic efficiency that's salient to the question.)


You can save yourself a ton of work by realizing that you basically know the answer in advance and don't need to do all this dynamic work.

std::string uint8_vector_to_hex_string(const vector<uint8_t>& v)
{
    std::string result;
    result.reserve(v.size() * 2);   // two digits per character

    static constexpr char hex[] = "0123456789ABCDEF";

    for (uint8_t c : v)
    {
        result.push_back(hex[c / 16]);
        result.push_back(hex[c % 16]);
    }

    return result;
}

In the spirit of "recognizing the algorithm", here's a separated algorithm for place-value formatting of numeric sequences. First the use case:

#include <iostream>
#include <string>
#include <vector>

// bring your own alphabet
constexpr char Alphabet[] = "0123456789ABCDEF";

// input
std::vector<unsigned char> const v { 31, 214, 63, 9 };

// output (Note: *our* responsibility to make allocations efficient)
std::string out;
out.reserve(v.size() * 2);

// the algorithm
place_value_format<char,     // output type
                      2,     // fixed output width
                     16>(    // place-value number base
    v.begin(), v.end(),      // input range
    std::back_inserter(out), // output iterator
    Alphabet);               // digit representation

Now the algorithm:

#include <algorithm>
#include <iterator>

template <typename Out, std::size_t NDigits, std::size_t Base,
          typename InItr, typename OutItr>
OutItr place_value_format(InItr first, InItr last, OutItr out, Out const * digits)
{
    for (; first != last; ++first)
    {
        Out unit[NDigits];

        auto val = *first;

        for (auto it = std::rbegin(unit); it != std::rend(unit); ++it)
        {
            *it = digits[val % Base];
            val /= Base;
        }

        out = std::copy(std::begin(unit), std::end(unit), out);
    }

    return out;
}


回答3:

If you don't mind using Boost in your project you can use boost::hex

#include <boost/algorithm/hex.hpp>

std::string uint8_vector_to_hex_string(const std::vector<uint8_t> & v) {
    std::string result;
    result.reserve(v.size() * 2);
    boost::algorithm::hex(v.begin(), v.end(), std::back_inserter(result));
    return result;
}


标签: c++ vector std