Convert hex to int

Started by
25 comments, last by monid233 12 years, 10 months ago
Hello, I am attempting to convert hex to an int.
I did some research, but have only found a way to convert, for example, string HEX = “fffefffe“; to an int.
However, I need to convert hex in form, for example, string HEX = “\x15\x03”; to an int.

I did some experimenting and came up with the following code:

int hex_to_int(string HEX) //Converts hex to an integer.
{
int x = 0;

char* _hex = new char[HEX.size()];

for(int i = 0; i < HEX.size(); i++)
{
_hex = HEX;
}
x = (int)*_hex;

delete _hex;

return x;
}

It is able to convert the first part, but I need to convert all of string = "\x15\x03"; and not just "\x15".

How would I go about accomplishing my goal?
Thanks in advance!
Advertisement

Hello, I am attempting to convert hex to an int.


It's good to get this stuff straight early on. Hexadecimal is merely one of many visual representations of an integer. The representation you're most familiar with is decimal (base10). 0x0A (hex) and 10 (decimal) are the same number -- the same 'int'.

I'm sure most people will understand what you mean, but your initial statement ("I am attempting to convert hex to an int") doesn't really make sense. Take some time to understand this, it will serve you well!

You appear to be using std::string to hold an array of bytes, rather than some text. While this is certainly do-able, the semantics of the word "string" in programming probably don't match up too well with what you're trying to do with the content of that container. I'd suggest switching to something like std::vector<unsigned char> instead.

Anyway, given an array of bytes, whether it be in a string, or a vector, or whatever else, the answer is to use memcpy:


#include <iostream>
#include <cstring>
#include <string>

int main()
{
const std::string s1 = "\x3F\xD0\x21\x4A";
const unsigned s2 = 0x4A21D03F; // NOTE: I'm on a little endian machine where sizeof(unsigned) == 4 and CHAR_BIT == 8. You probably are too :)


unsigned s1_as_int = 0;
std::memcpy(&s1_as_int, s1.c_str(), 4);

std::cout << (s2 == s1_as_int) << '\n';

return 0;
}


However! I suspect what you've done is read in data from a file in to a string and are subsequently attempting to convert it to properly typed and structured data.

If you have a "binary file", it might be easier to read it piece by piece in to ints and other datatypes. This assumes that the machine and compiler that wrote the file matches your machine and compiler, but if you provide a little more information some better targeted advice can be offered in this area, I'm sure. So what's your higher-level goal?
string hex = "\0x15\x14\0x13" is basically 3 chars in a row, so you'd do something similar to:

unsigned i = 0;

for (size_t ct = 0; ct < hex.size(); ++ct)
{
i <<= 8;

i += hex[ct];
}

This is just off of the top of my head, and hex.size() cannot be bigger than sizeof(unsigned).
[font="arial, verdana, tahoma, sans-serif"]

It's good to get this stuff straight early on. Hexadecimal is merely one of many visual representations of an integer. The representation you're most familiar with is decimal (base10). 0x0A (hex) and 10 (decimal) are the same number -- the same 'int'.

I'm sure most people will understand what you mean, but your initial statement ("I am attempting to convert hex to an int") doesn't really make sense. Take some time to understand this, it will serve you well!

Thanks for clearing that up![/font]
You have guessed correctly, I am early on in this ;)



You appear to be using std::string to hold an array of bytes, rather than some text. While this is certainly do-able, the semantics of the word "string" in programming probably don't match up too well with what you're trying to do with the content of that container. I'd suggest switching to something like std::vector<unsigned char> instead.

I realized string isn't the best for this, but...


1. However! I suspect what you've done is read in data from a file in to a string and are subsequently attempting to convert it to properly typed and structured data.

2. If you have a "binary file", it might be easier to read it piece by piece in to ints and other datatypes. This assumes that the machine and compiler that wrote the file matches your machine and compiler, but if you provide a little more information some better targeted advice can be offered in this area, I'm sure. So what's your higher-level goal?

1. You are correct.

2. My higher goal is to create a tool that extracts and recompiles the data files for a 12 year old game.


Anyway, given an array of bytes, whether it be in a string, or a vector, or whatever else, the answer is to use memcpy:


#include <iostream>
#include <cstring>
#include <string>

int main()
{
const std::string s1 = "\x3F\xD0\x21\x4A";
const unsigned s2 = 0x4A21D03F; // NOTE: I'm on a little endian machine where sizeof(unsigned) == 4 and CHAR_BIT == 8. You probably are too :)


unsigned s1_as_int = 0;
std::memcpy(&s1_as_int, s1.c_str(), 4);

std::cout << (s2 == s1_as_int) << '\n';

return 0;
}

I tried this code. It worked!


int hex_to_int(const string HEX) //Converts hex to an integer.
{

unsigned x = 0;
memcpy(&x, HEX.c_str(), HEX.size());


return x;
}
Thank you very much!





string hex = "\0x15\x14\0x13" is basically 3 chars in a row, so you'd do something similar to:

unsigned i = 0;

for (size_t ct = 0; ct < hex.size(); ++ct)
{
i <<= 8;

i += hex[ct];
}

This is just off of the top of my head, and hex.size() cannot be bigger than sizeof(unsigned).

Unfortunately, this doesn't work.
"\x15\x03" should convert into 789. When I ran my program with this code, it gave me 5379.




Thank you all very much!

Don't mind if I follow this up with a question concerning how to convert, say, 789 back into "\x15\x03"?

I tried this code. It worked!


int hex_to_int(const string HEX) //Converts hex to an integer.
{

unsigned x = 0;
memcpy(&x, HEX.c_str(), HEX.size());


return x;
}
Thank you very much!

I'm glad. But take care to understand that this will fall apart if HEX.size() != sizeof(unsigned). If HEX contained a long string chars, for example, the result of that function call would be disastrous as the memcpy call would copy bytes over memory outside of x -- who knows where?! Ideally, when copying from an array of bytes in to some other primitive data type using memcpy, the third argument should be "sizeof(x)" if the first argument is "&x". This ensures that memcpy can't possibly write to places it's not supposed to. I should have done this in my initial example, really :/

For each chunk of bytes that you wish to 'decode' in to a primitive data type, you must know the number of bytes in that chunk and a C(++) data type that is the same size as that chunk.

I also briefly mentioned the phrase "little endian" in my last post. It would be worth spending a little bit of time understanding what big endian and little endian mean, and how they relate to reading in so-called binary data. Feel free to ask, but google will probably get you rather far.

The other thing I mentioned is that in C or C++, you can use fread() and std::istream::read() to directly read the bytes from a file in to a primitive data type.

For example:


unsigned get_unsigned_from_file(const std::string &filename)
{
unsigned ret = 0;

// TODO: add error checking!
std::ifstream in(filename.c_str(), std::ios::binary);
in.read(reinterpret_cast<char *>(&ret, sizeof(ret));
return ret;
}


An equivalent using fread(), which can be used in C as well:


unsigned get_unsigned_from_file(const char *filename)
{
unsigned ret = 0;
FILE *fptr = fopen(filename, "rb");

if (fptr)
{
/* TODO: add error checking! */
fread(&ret, 4, 1, fptr);
fclose(fptr);
}
return ret;
}




Don't mind if I follow this up with a question concerning how to convert, say, 789 back into "\x15\x03"?
[/quote]
It already is! If you don't understand what I mean by that, ask :)

If you want to put the value in to an array of bytes e.g. std::vector<unsigned char>, make sure the vector is big enough and then use memcpy to copy the data over the elements of the vector (not the over the vector itself).
[font="arial, verdana, tahoma, sans-serif"]

I'm glad. But take care to understand that this will fall apart if HEX.size() != sizeof(unsigned). If HEX contained a long string chars, for example, the result of that function call would be disastrous as the memcpy call would copy bytes over memory outside of x -- who knows where?!

Don't worry, the tool checks to make sure it is a valid data file for this game, so it'll fall apart only if something messed up badly.
Or did you mean that this could fall apart if the compiler uses different sizes for the involved data types?



1. For each chunk of bytes that you wish to 'decode' in to a primitive data type, you must know the number of bytes in that chunk and a C(++) data type that is the same size as that chunk.

2. I also briefly mentioned the word "little endian" in my last post. It would be worth spending a little bit of time understanding what big endian and little endian mean, and how they relate to reading in so-called binary data. Feel free to ask, but google will probably get you rather far.

1. Well, the only chunks of bytes I need to decode are three parts of the header..

2. Yes, I know what little and big endian are :)



The other thing I mentioned is that in C or C++, you can use fread() and std::istream::read() to directly read the bytes from a file in to a primitive data type.

For example:


unsigned get_unsigned_from_file(const std::string &filename)
{
unsigned ret = 0;

// TODO: add error checking!
std::ifstream in(filename.c_str(), std::ios::binary);
in.read(reinterpret_cast<char *>(&ret, sizeof(ret));
return ret;
}


An equivalent using fread(), which can be used in C as well:


unsigned get_unsigned_from_file(const char *filename)
{
unsigned ret = 0;
FILE *fptr = fopen(filename, "rb");

if (fptr)
{
/* TODO: add error checking! */
fread(&ret, 4, 1, fptr);
fclose(fptr);
}
return ret;
}


I'll have to read up on fread...
I'm using this for reading in files:

FILE* file = fopen("myfile", "r");
int c = 0;

if(!file)
{
cout << "Failed to load file!\n";
return 0;
}

while (c != EOF)
{
c = getc(file);

//Do stuff.
}

fclose(file);

Any chance bytes can be read in directly into primitive data types using this code?




It already is! If you don't understand what I mean by that, ask

If you want to put the value in to an array of bytes e.g. std::vector<unsigned char>, make sure the vector is big enough and then use memcpy to copy the data over the elements of the vector (not the over the vector itself).

I'm asking ;)[/font]

Don't worry, the tool checks to make sure it is a valid data file for this game,

Ok, but you need to check that you aren't decoding too much or too little data.


so it'll fall apart only if something messed up badly.
[/quote]
There are two kinds of falling apart: crashing, and failing gracefully and (ideally) informing the user with details of the problem. Make sure when things fall apart, they're doing so in the second kind of way.


Or did you mean that this could fall apart if the compiler uses different sizes for the involved data types?
[/quote]
That's one way in which things might crash, yes. Bad!



For each chunk of bytes that you wish to 'decode' in to a primitive data type, you must know the number of bytes in that chunk and a C(++) data type that is the same size as that chunk.

1. Well, the only chunks of bytes I need to decode are three parts of the header..
[/quote]
And do you know the size of each chunk? Have you found C(++) data types that are of an equivalent size? Just making sure you've dotted the 'i's and crossed the 't's...



I'll have to read up on fread...
I'm using this for reading in files:

FILE* file = fopen("myfile", "r");
int c = 0;

if(!file)
{
cout << "Failed to load file!\n";
return 0;
}

while (c != EOF)
{
c = getc(file);

//Do stuff.
}

fclose(file);

Any chance bytes can be read in directly into primitive data types using this code?
[/quote]
With some changes, yes. Note that one of my code snippets reads data through a FILE* interface, like yours does. fread() takes a FILE* as one of its arguments.




It already is! If you don't understand what I mean by that, ask

I'm asking ;)[/quote]

Memory in all computers (worth talking about here) consists of bytes (usually defined to be 8 bits). To a reasonable approximation, every variable that exists and is used in a C(++) program will be represented by 1 or more bytes somewhere in your computer's memory.

The memcpy function literally takes some bytes from one place and copies them to another. In the first post where I mentioned memcpy, I copied some bytes out of the character buffer of the std::string in to the memory associated with an 'unsigned' variable.

memcpy doesn't change the values of the bytes as it copies.

So let's return to your question:

[...] how to convert, say, 789 back into "\x15\x03"?
[/quote]

789 and "\x15\x03" are the same thing. They are merely different representations of the same value.

If you have a std::string containing "\x16\x03" and an unsigned containing 789 in your program, then somewhere in different parts of your computer's memory, there are two identical copies of the same small byte sequence.

If you want to put the bytes from an unsigned in to a string, you can use memcpy.

Again, I recommend using vector<unsigned char> instead. And please be sure to preallocate enough memory in the vector/string buffer before doing the memcpy operation.

But again, there's a function fwrite(), that does the opposite of fread(), so you might not need to go through an intermediate buffer and may be able to write the bytes out of primitives in to your file directly.
Assuming you can rely on the fact that the string will always be \x then 2 decimal digits (being a max of 15)
then this should work:

template <class numberType>
bool NumberFromString(numberType& out, const std::string& s, std::ios_base& (*f)(std::ios_base&) = std::dec)
{
std::istringstream iss(s);
return !(iss >> f >> out).fail();
}

int main()
{

std::string hex("\\x15\\x03\\x01");
std::stack<std::string> numbers;
std::string number;

while(!hex.empty())
{
hex = hex.substr(2);
number = hex.substr(0, 2);
hex = hex.substr(2);
numbers.push(number);
}

int num = 0;
int current = 0;
int multiple = 1;
while(!numbers.empty())
{
NumberFromString<int>(current, numbers.top());
num += current * multiple;
multiple *= 16;
numbers.pop();
}
// "num" should be integer value of the hex string
}


Not the fastest, no error checking either. The idea is, you convert the long string into a bunch of numbers and put them on a stack, the left (most significant number) most being on the bottom and the right most (least significant) being on the top. The far right number is how many 1s there are (hence multiple starting at 1). So you multiply it by 1 and add it to the total. The next number across is how many 16s there are (multiple is now 16). Next across is how many 256s (1*16*16), next along would be how many 4096s (1*16*16*16) and so on.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.



int main()
{

std::string hex("\\x15\\x03\\x01");
...


Note that the O.P's string is very different from this, in meaning (number of slashes).

Ok, but you need to check that you aren't decoding too much or too little data.

Luckily, the person who designed this data file seperated these parts with padding :)



There are two kinds of falling apart: crashing, and failing gracefully and (ideally) informing the user with details of the problem. Make sure when things fall apart, they're doing so in the second kind of way.

That's one way in which things might crash, yes. Bad!

Oh boy, error checking. Very important.



And do you know the size of each chunk? Have you found C(++) data types that are of an equivalent size? Just making sure you've dotted the 'i's and crossed the 't's...

That's a problem. The size of the chunks can be anything. But, as said, these chunks are surrounded by padding, so it's not too hard to tell what the size of the chunks are at runtime.



With some changes, yes. Note that one of my code snippets reads data through a FILE* interface, like yours does. fread() takes a FILE* as one of its arguments.

I noticed that. I've yet to take a look at how to tell where fread() should start reading.



So let's return to your question:

[...] how to convert, say, 789 back into "\x15\x03"?


789 and "\x15\x03" are the same thing. They are merely different representations of the same value.

If you have a std::string containing "\x16\x03" and an unsigned containing 789 in your program, then somewhere in different parts of your computer's memory, there are two identical copies of the same small byte sequence.

If you want to put the bytes from an unsigned in to a string, you can use memcpy.

Again, I recommend using vector<unsigned char> instead. And please be sure to preallocate enough memory in the vector/string buffer before doing the memcpy operation.

But again, there's a function fwrite(), that does the opposite of fread(), so you might not need to go through an intermediate buffer and may be able to write the bytes out of primitives in to your file directly.
[/quote]
Here's the code I tried:

unsigned number = 728;
std::vector<unsigned char> v;
v.resize(sizeof(number));
memcpy(&v[0], &number, sizeof(number));

std::FILE *file = std::fopen("test.pwp", "w");
std::fwrite(&v, sizeof(unsigned char), v.size() - 1, file);
std::fclose(file);

It doesn't seem to work properly.




Assuming you can rely on the fact that the string will always be \x then 2 decimal digits (being a max of 15)
then this should work:

[...]

Not the fastest, no error checking either. The idea is, you convert the long string into a bunch of numbers and put them on a stack, the left (most significant number) most being on the bottom and the right most (least significant) being on the top. The far right number is how many 1s there are (hence multiple starting at 1). So you multiply it by 1 and add it to the total. The next number across is how many 16s there are (multiple is now 16). Next across is how many 256s (1*16*16), next along would be how many 4096s (1*16*16*16) and so on.

Tried it...
Apparently it doesn't accept "\x15\x03", which is what is being read in from the file.
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr
Aborted (core dumped)
[/quote]
Additionally, I can't rely on the fact that it will always be a max of 15.

This topic is closed to new replies.

Advertisement