0

I'd like to take the next two hex characters from a stream and store them as the associated associated hex->decimal numeric value in a char.

So if an input file contains 2a3123, I'd like to grab 2a, and store the numeric value (decimal 42) in a char.

I've tried

char c;
instream >> std::setw(2) >> std::hex >> c;

but this gives me garbage (if I replace c with an int, I get the maximum value for signed int).

Any help would be greatly appreciated! Thanks!

edit: I should note that the characters are guaranteed to be within the proper range for chars and that the file is valid hexadecimal.

4

2 回答 2

3

OK I think dealing with ASCII decoding is a bad idea at all and does not really answer the question.

I think your code does not work because setw() or istream::width() works only when you read to std::string or char*. I guess it from here

How ever you can use the goodness of standard c++ iostream converters. I came up with idea that uses stringstream class and string as buffer. The thing is to read n chars into buffer and then use stringstream as a converter facility.

I am not sure if this is the most optimal version. Probably not.

Code:

#include <iostream>
#include <sstream>

int main(void){
    int c;
    std::string buff;
    std::stringstream ss_buff;
    std::cin.width(2);
    std::cin >> buff;
    ss_buff << buff;
    ss_buff >> std::hex >> c;
    std::cout << "read val: " << c << '\n';
}

Result:

luk32@genaker:~/projects/tmp$ ./a.out 
0a10
read val: 10
luk32@genaker:~/projects/tmp$ ./a.out 
10a2
read val: 16
luk32@genaker:~/projects/tmp$ ./a.out 
bv00   
read val: 11
luk32@genaker:~/projects/tmp$ ./a.out 
bc01
read val: 188
luk32@genaker:~/projects/tmp$ ./a.out
01bc
read val: 1

And as you can see not very error resistant. Nonetheless, works for the given conditions, can be expanded into a loop and most importantly uses the iostream converting facilities so no ASCII magic from your side. C/ASCII would probably be way faster though.

PS. Improved version. Uses simple char[2] buffer and uses non-formatted write/read to move data thorough the buffer (get/write as opposed to operator<</operator>>). The rationale is pretty simple. We do not need any fanciness to move 2 bytes of data. We ,however, use formatted extractor to make the conversion. I made it a loop version for the convenience. It was not super simple though. It took me good 40 minutes of fooling around to figure out very important lines. With out them the extraction works for 1st 2 characters.

#include <iostream>
#include <sstream>

int main(void){
    int c;
    char* buff = new char[3];
    std::stringstream ss_buff;
    std::cout << "read vals: ";
    std::string tmp;
    while( std::cin.get(buff, 3).gcount() == 2 ){
       std::cout << '(' << buff << ") ";
       ss_buff.seekp(0); //VERY important lines
       ss_buff.seekg(0); //VERY important lines
       ss_buff.write(buff, 2);
       if( ss_buff.fail() ){ std::cout << "error\n"; break;}
       std::cout << ss_buff.str() << ' ';
       ss_buff >> std::hex >> c;
       std::cout << c << '\n';
    }
    std::cout << '\n';
    delete [] buff;
}

Sample output:

luk32@genaker:~/projects/tmp$ ./a.out
read vals: 0aabffc
(0a) 0a 10
(ab) ab 171
(ff) ff 255

Please note, the c was not read as intended.

I found everything needed here http://www.cplusplus.com/reference/iostream/

于 2012-09-20T02:23:45.517 回答
0

You can cast a Char to an int and the int will hold the ascii value of the char. For example, '0' will be 48, '5' will be 53. The letters occur higher up so 'a' will be cast to 97, 'b' to 98 etc. So knowing this you can take the int value and subtract 48, if the result is greater than 9, subtract another 39. Then char 0 will have been turned to int 0, char 1 to int 1 all the way up to char a being set to int 10, char b to int 11 etc.

Next you will need to multiply the value of the first by 16 and add it to the second to account for the bit shift. Using your example of 2a.

char 2 casts to int 50. Subtract 48 and get 2. Multiply by 16 and get 32. char a casts to int 97. Subtract 48 and get 49, this is higher than 9 so subtract another 39 and get 10. Add this to the end result of the last one (32) and you get 42.

Here is the code:

int HexToInt(char hi, char low)
{
    int retVal = 0;
    int hiBits = (int)hi;
    int loBits = (int)low;
    retVal = Convert(hiBits) * 16 + Convert(loBits);
    return retVal;
}

int Convert(int in)
{
    int retVal = in - 48;
    //If it was not a digit
    if(retVal > 10)
        retVal = retVal - 7;
    //if it was not an upper case hex didgit
    if(retVal > 15)
        retVal = retVal - 32;
    return retVal;  
}

The first function can actually be written as one line thus:

 int HexToInt(char hi, char low)
 {
    return Convert((int)hi) * 16 + Convert((int)low);       
 }

NOTE: This only accounts for lower case letters and only works on systems that uses ASCII, i.e. Not IBM ebcdic based systems.

于 2012-09-20T01:56:08.030 回答