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?
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();
}
...
(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;
}
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;
}