C++ convert string to hexadecimal and vice versa
Solution 1
A string like "Hello World" to hex format: 48656C6C6F20576F726C64.
Ah, here you go:
#include <string>
std::string string_to_hex(const std::string& input)
{
static const char hex_digits[] = "0123456789ABCDEF";
std::string output;
output.reserve(input.length() * 2);
for (unsigned char c : input)
{
output.push_back(hex_digits[c >> 4]);
output.push_back(hex_digits[c & 15]);
}
return output;
}
#include <stdexcept>
int hex_value(unsigned char hex_digit)
{
static const signed char hex_values[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
int value = hex_values[hex_digit];
if (value == -1) throw std::invalid_argument("invalid hex digit");
return value;
}
std::string hex_to_string(const std::string& input)
{
const auto len = input.length();
if (len & 1) throw std::invalid_argument("odd length");
std::string output;
output.reserve(len / 2);
for (auto it = input.begin(); it != input.end(); )
{
int hi = hex_value(*it++);
int lo = hex_value(*it++);
output.push_back(hi << 4 | lo);
}
return output;
}
(This assumes that a char has 8 bits, so it's not very portable, but you can take it from here.)
Solution 2
string ToHex(const string& s, bool upper_case /* = true */)
{
ostringstream ret;
for (string::size_type i = 0; i < s.length(); ++i)
ret << std::hex << std::setfill('0') << std::setw(2) << (upper_case ? std::uppercase : std::nouppercase) << (int)s[i];
return ret.str();
}
int FromHex(const string &s) { return strtoul(s.c_str(), NULL, 16); }
Solution 3
Using lookup tables and the like works, but is just overkill, here are some very simple ways of taking a string to hex and hex back to a string:
#include <stdexcept>
#include <sstream>
#include <iomanip>
#include <string>
#include <cstdint>
std::string string_to_hex(const std::string& in) {
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (size_t i = 0; in.length() > i; ++i) {
ss << std::setw(2) << static_cast<unsigned int>(static_cast<unsigned char>(in[i]));
}
return ss.str();
}
std::string hex_to_string(const std::string& in) {
std::string output;
if ((in.length() % 2) != 0) {
throw std::runtime_error("String is not valid length ...");
}
size_t cnt = in.length() / 2;
for (size_t i = 0; cnt > i; ++i) {
uint32_t s = 0;
std::stringstream ss;
ss << std::hex << in.substr(i * 2, 2);
ss >> s;
output.push_back(static_cast<unsigned char>(s));
}
return output;
}
Solution 4
I think there is a much simpler and more elegant solution. Some of the above-mentioned methods may even throw unhandled exceptions in some cases. Here is a fool-proof (as in never goes wrong) and very fast code. Just try it and compare the results in terms of speed and compactness:
#include <string>
// Convert string of chars to its representative string of hex numbers
void stream2hex(const std::string str, std::string& hexstr, bool capital = false)
{
hexstr.resize(str.size() * 2);
const size_t a = capital ? 'A' - 1 : 'a' - 1;
for (size_t i = 0, c = str[0] & 0xFF; i < hexstr.size(); c = str[i / 2] & 0xFF)
{
hexstr[i++] = c > 0x9F ? (c / 16 - 9) | a : c / 16 | '0';
hexstr[i++] = (c & 0xF) > 9 ? (c % 16 - 9) | a : c % 16 | '0';
}
}
// Convert string of hex numbers to its equivalent char-stream
void hex2stream(const std::string hexstr, std::string& str)
{
str.resize((hexstr.size() + 1) / 2);
for (size_t i = 0, j = 0; i < str.size(); i++, j++)
{
str[i] = (hexstr[j] & '@' ? hexstr[j] + 9 : hexstr[j]) << 4, j++;
str[i] |= (hexstr[j] & '@' ? hexstr[j] + 9 : hexstr[j]) & 0xF;
}
}
#include <iostream>
int main()
{
std::string s = "Hello World!";
std::cout << "original string: " << s << '\n';
stream2hex(s, s);
std::cout << "hex format: " << s << '\n';
hex2stream(s, s);
std::cout << "original one: " << s << '\n';
}
and the result is:
original string: Hello World!
hex format: 48656C6C6F20576F726C6421
original one: Hello World!
Solution 5
As of C++17 there's also std::from_chars. The following function takes a string of hex characters and returns a vector of T:
#include <charconv>
template<typename T>
std::vector<T> hexstr_to_vec(const std::string& str, unsigned char chars_per_num = 2)
{
std::vector<T> out(str.size() / chars_per_num, 0);
T value;
for (std::size_t i = 0; i < str.size() / chars_per_num; i++) {
std::from_chars<T>(
str.data() + (i * chars_per_num),
str.data() + (i * chars_per_num) + chars_per_num,
value,
16
);
out[i] = value;
}
return out;
}
Sebtm
Updated on May 19, 2021Comments
-
Sebtm about 3 years
What is the best way to convert a string to hex and vice versa in C++?
Example:
- A string like
"Hello World"
to hex format:48656C6C6F20576F726C64
- And from hex
48656C6C6F20576F726C64
to string:"Hello World"
- A string like
-
Billy ONeal almost 14 years+1, but I would implement the second in terms of
istringstream
-- strtoul is not a standard library function. -
Sebtm almost 14 yearsWhy FromHex returns as an int? Should return as a string.
-
Krevan almost 14 years@Sebtm: if you call FromHex("10"); it will return 16, since 10 in hex is 16
-
liwp over 12 yearsI had to mask the shifted lut index, i.e. (c >> 4) & 0x0F to make this work for me.
-
Timmmm over 11 yearsThis is pretty nice. How would you do it the other way?
-
BatchyX over 11 yearsThis does not work with small character values (e.g. If your string is \x01\x02\x03)
-
Mahmut EFE over 11 yearsit works, change your string like "\\x01\\x02\\x03". because compiler doesnt compile "\x" character.
-
Colorless Photon almost 11 years@Abyx But isn't C++ a superset of C? Doesn't this mean that we can use some stuff from C?
-
richvdh almost 11 yearsIt seems to work for small character values, but not large ones. test="\xf0" should encode to "f0", but it gives "fffffff0".
-
richvdh almost 11 yearsI take that back, it does fail on small character values too. std::setw() only has an effect for the next write.
-
richvdh almost 11 yearsDid you actually try running this? it's nowhere near correct.
-
atoMerz over 10 years
toHex
function gave me strange results. It turns out you should convert the byte into an unsigned 8-bit integer (UINT8
in windows). -
thomthom over 8 yearsVS2013 complained about
uint32_t
- had to add<cstdint>
. -
thomthom over 8 yearsI found that
<< std::setw(2)
got reset after each read - and I had to use it inside thefor
loop. I looked up the docs and they also states that the width is reset in many scenarios: en.cppreference.com/w/cpp/io/manip/setw -
X-Istence over 8 yearsInteresting, I never ran into that issue while using the function, but looking at the docs you are correct. Thanks for the fix :-)
-
43.52.4D. about 8 yearsDoes not work with string that contain binary such as 0
-
43.52.4D. about 8 years@Erik Hvatum Does this convert an std::string to hex string? I don't understand what you mean by "Binary data sequence". Could you post an example of calling this function?
-
polfosol ఠ_ఠ about 8 years@43.52.4D. It works perfectly. See The live example
-
43.52.4D. about 8 yearsNo, I meant actual binary. When I tried this algorithm on a file, some characters did not get converted to HEX properly.
-
Erik Hvatum about 8 years@43.52.4D The dataToHexString function takes 3 arguments. The first is a pointer to the first byte of memory you wish to have rendered in hex, the second is a pointer to the byte of memory after the last you wish to have as text. The third is a string which is modified to contain the text. For example: vector<uint8_t> a; a.push_back(1); a.push_back(255); string s; dataToHex(a.data(), a.data()+a.size(), s); cout << s << '\n'; // "00ff"
-
polfosol ఠ_ఠ about 8 yearsDid you read the file in binary mode? Can you show me your code or paste it somewhere? @43.52.4D.
-
polfosol ఠ_ఠ about 8 years
-
43.52.4D. about 8 yearsHmmm idk, personally when I tried it, it didn't seem to convert all bytes properly. Does it work when the data contains NULL characters?
-
polfosol ఠ_ఠ about 8 yearsAre you serious?? In the second example the string had 8 NULL characters. @43.52.4D.
-
43.52.4D. about 8 yearsOkay, I'm glad it works fine :) Maybe I had a bug in my code.
-
slawekwin almost 8 yearsinstead of platform specific
UINT8
you can cast(int)(unsigned char)
-
yau over 7 years@liwp: you need that mask even after correctly casting to unsigned char?
-
Leśny Rumcajs over 5 yearsI like to browse through C / C++98 answers to finally get to modern C++ answer. :)
-
thomthom over 5 years@richvdh - did you find out why
fffffff0
would be returned? EDIT: Found the solution below: stackoverflow.com/a/16125797/486990 the double static cast was needed. -
allo almost 5 yearsDoes it handle '\0' correctly? I seem to get too wrong results when the string contains null bytes.
-
Sean over 4 yearsCan someone explain why do we have to do in the other complicated ways? I felt this solution is enough.
-
theicfire over 4 yearsThis is a lot simpler because it's printed inside of the function. The other answers are more complicated because they return a solution, and don't print anything.
-
muxator over 2 yearsThis function correctly handles binary data, but in order to feed that data, you'll have to use the two-parameters version of
std::string
constructor (see en.cppreference.com/w/cpp/string/basic_string/basic_string):std::string(const char* s, size_type count);
Otherwise your test string will be truncated at the first null byte:string_to_hex(std::string("\x00\x01", 2)) == "0001" // correct
, versusstring_to_hex("\x00\x01") == "" // you are really feeding an empty string to the function
-
IInspectable about 2 years@sea You'll find that part of the added complexity is due to correctness requirements. This solution glosses over this requirement. You'll find out once your input is, say,
"Hello\nWorld"
. This produces a string of hexadecimal digits that can no longer be unambiguously converted back. Indeed, for every problem there is a solution that's simple, intuitive, and wrong.