Need help in extracting field values for hexadecimal string

Pages: 12

Hello,
As shown in below C++ program,
I have a string with hexadecimal digits
Right most two hex digits are extracted into hex_byte_str[3] variable

I need expressions for Calculating x and y integer values from above two hex digits please.
x =
y =

These x, y int variables values need to be calculated from string variable hex_byte_str

Comments in this program are giving additional information.

Thanks and best regards,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// hex_str.cpp

#include <iostream>

using std::cout;
using std::endl;

int main(void)
{                    
	char hex_ch_str[7] = "F0FFC3";   // digits are read from right to left

	char hex_byte_str[3];

	hex_byte_str[0] = hex_ch_str[4];
	hex_byte_str[1] = hex_ch_str[5];
	hex_byte_str[2] = '\0';

	// outputs Hexa-decimal string C3  
	// it is equivalent to binary string 11000011
	cout << "Hex byte str: " << hex_byte_str << endl;  // prints C3

	int x;  // second (from left) binary digit to be extracted from 11000011
	int y;  // last six binary digits to be extracted from 11000011

	// x value must be second binary digit (from left)to be extracted from 11000011
	
x =  ???? ;
	// y value must be last six digits 000011 to be extracted from 11000011
y =  ????? ;

// x an y values are to be calculated from hex  C3 value of hex_byte_str  variable

	cout << "x : " << x << endl;   // it should print decimal value of 1
	cout << "y : " << y << endl;   // it should print decimal value of 3 (is the decimal value of last six binary digits 000011 of hex C3 i.e. binary digits 11000011

	return 0;
}

Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <bitset>
#include <string>

int main()
{
    const char hex_ch_str[7] = "F0FFC3" ;
    const char* const hex_byte_str = hex_ch_str + 4 ;

    std::cout << "hex_byte_str: " << hex_byte_str << '\n' ; // outputs Hexa-decimal string C3

    const std::bitset<8> bits( std::stoull( hex_byte_str, 0, 16 ) ) ; // bits in hex_byte_str
    std::cout << bits << '\n' ; // 11000011

    const int x = bits[ bits.size() - 2 ] ; // second (from left) binary digit to be extracted from 11000011
    const std::size_t scnt = bits.size() - 6 ; // pos from left of the start of the last six binary digits
    const int y = ( (bits<<scnt) >> scnt ).to_ulong() ;  // last six binary digits to be extracted from 11000011

    std::cout << "x == " << x << '\n' << "y == " << y << '\n' ; // 1 3
}

http://coliru.stacked-crooked.com/a/60d3344307437b07
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
using namespace std;

void extract( const string &str, int &x, int &y )
{
   int i = stoi( str.substr( 4, 2 ), 0, 16 );
   x = ( i >> 6 ) & 1;
   y = i & 63;
}

int main()
{
   int x, y;
   extract( "F0FFC3", x, y );
   cout << x << '\n' << y << '\n';
}
1
3
Hello JLBorges and lastchance,

Many, many thanks for the excellent C++ code.

I need to learn these kinds of bits shifting and <bitset> features.

Is there any excellent C++ book (or a website) that explains these bits manipulation techniques
along with many bit manipulation programming exercises and their solutions please?

Again, I can never thank you enough for your brilliant C++ techniques.

Best regards,
For <bitset> see:
https://cplusplus.com/reference/bitset/bitset/

For lastchance's solution, this doesn't use anything special - just standard bit manipulation using shift (>> is right shift here) and bit-wise and (&).

See:
https://www.learncpp.com/cpp-tutorial/bitwise-operators/
https://www.geeksforgeeks.org/bitwise-operators-in-c-cpp/
https://www.programiz.com/cpp-programming/bitwise-operators
Referring to the following statement:
 
char hex_ch_str[7] = "F0FFC3";  


Hex string "F0FFC3" is equivalent to 24 binary characters:
1111 0000 1111 1111 1100 0011

I need to remove left most 1 bit and right most 7 bits (100 0011) for the above calculated x, y.
Then I will have 16 bits
111 0000 1111 1111 1

I need to regroup these 16 bits as Hex chars and return as Hex string:
E1FF
(from above 16 digits, 1110 0001 1111 1111)

I am thinking about how to C++ code and extract hex string E1FF.
Please help with these code lines.

Using your help with these, I will be able to code many other such bit manipulation tasks.

Again, many thanks for your help.
Best regards,











as long as you are not going huge, its easy to do this using either unsigned 64 bit integers or if yuo want, bitset.
convert the hex text into an integer (you can do this any of many ways, I think you are past that part of the problem?).
then just & it with 01111.... to remove the left most bit.
then shift it (>> and << operators have a bitwise non-stream meaning here) to remove the right most bits.
return what is left, printed into hex format.

if you need more than 64 bits, using the integer isnt going to work, and you will have to stick to bitset or some other container and possibly do some hands-on work to cook up the output string.

if you didn't know binary and hex are related at the half-byte.
that is, 0xFC in binary is the same as F in binary followed by C in binary. A 16 value lookup table can then convert hex to binary if you need that. But you rarely want to work in pure binary, usually you do bit work on a bigger entity, at least a byte worth.
Last edited on
Just a point. If you're going to be doing bit shifting etc on an integer, use an unsigned type. If you use signed (eg int) then you could get 'interesting' results if the leftmost bit is 1 when sign extension comes into play...
Hello jonnin and seeplus,
Thanks so much for excellent information.
I have created a bitset<24> as shown below.

Can I select middle 16 bits, ignoring left one bit and right seven bits and get E1FF hex string?

Using what C++ expression?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <bitset>
#include <string>

using std::bitset;
using std::cout;
using std::endl;

int main(void)
{
	const unsigned int HEX_STR_BITS = 24;

	const char hex_ch_str[7] = "F0FFC3";

	bitset<HEX_STR_BITS> hex_bits_24(hex_ch_str);
	
	return 0;
}
Last edited on
I wrote following code and Visual C++ 2022 is compiling without errors but,
the program is aborting during runtime,

I have debugged by stepping through the program and error is occurring on code line:


1
2
3

	bitset<HEX_STR_BITS> hex_bits(hex_ch_str);



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// hex_str.cpp

#include <iostream>
#include <bitset>
#include <string>

using std::bitset;
using std::cout;
using std::endl;
using std::string;

int main(void)
{
	const unsigned int HEX_STR_BITS = 48;

	const char hex_ch_str[7] = "F0FFC3";

	bitset<HEX_STR_BITS> hex_bits(hex_ch_str);

	string str = hex_bits.to_string().substr(7, hex_bits.size() - 7);

	cout << str << endl;
	
	return 0;
}


I wrote the above code using following code at the bottom of URL


https://stackoverflow.com/questions/17857596/how-to-convert-a-range-subset-of-bits-in-a-c-bitset-to-a-number



1
2
3
4
5
6
7
8
9
10

bitset<32> bs(string("1011"));
cout << bs.to_ullong() << endl;

// take a range - 2 last bits in this case
string s = bs.to_string().substr(bs.size() - 2);

bitset<32> bs1(s);
cout << bs1.to_ullong() << endl;



Last edited on
you can't pull a subset out. you can pull any one bit out and create a new integer or bitset in a loop, but you don't need to: bitset supports the and and shift that I showed you. And off the left side, shift off the right side. what remains is your center bits.
Last edited on
> error is occurring on code line:
> bitset<HEX_STR_BITS> hex_bits(hex_ch_str);

We can't directly initialise a std::bitset with a string of hexadecimal digits. Every character in the string used to initialise a std::bitset must be '0' or '1'; if not, the constructor would throw.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <bitset>
#include <string>
#include <climits>

const char* hex_char_to_bin_str( char ch )
{
    switch(ch)
    {
        case '0': return "0000";
        case '1': return "0001";
        case '2': return "0010";
        case '3': return "0011";
        case '4': return "0100";
        case '5': return "0101";
        case '6': return "0110";
        case '7': return "0111";
        case '8': return "1000";
        case '9': return "1001";
        case 'A': case 'a' : return "1010";
        case 'B': case 'b' : return "1011";
        case 'C': case 'c' : return "1100";
        case 'D': case 'd' : return "1101";
        case 'E': case 'e' : return "1110";
        case 'F': case 'f' : return "1111";
        default: throw std::invalid_argument( "bad hex digit" ) ;
    }
}

std::string hex_str_to_bin_str( const std::string& hex_str )
{
    std::string bin_str ;
    for( char ch : hex_str ) bin_str += hex_char_to_bin_str(ch) ;
    return bin_str ;
}

int main()
{
    const unsigned int HEX_STR_BITS = 48;
    const char hex_ch_str[7] = "F0FFC3";
    static_assert( HEX_STR_BITS == sizeof(hex_ch_str) * CHAR_BIT - CHAR_BIT ) ; // sanity check

    // convert hex_ch_str to an unsigned long long and construct the bitset using that
    // this is convenient if HEX_STR_BITS is small enough (typically <= 64)
    const std::bitset<HEX_STR_BITS> hex_bits( std::stoull( hex_ch_str, nullptr, 16 ) );
    const std::string str = hex_bits.to_string().substr( 7, hex_bits.size() - 7 );
    std::cout << str << '\n' ;

    // or convert hex_ch_str to a string of binary digits and construct the bitset using that
    const std::bitset<HEX_STR_BITS> hex_bits2( hex_str_to_bin_str(hex_ch_str) );
    const std::string str2 = hex_bits.to_string().substr( 7, hex_bits.size() - 7 );
    std::cout << str2 << '\n' ;
}

http://coliru.stacked-crooked.com/a/a13a33b9240dfe69
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
using namespace std;

unsigned long long bitRange( const string &hexstring, int lowbit, int numbits )
{
   unsigned long long i = stoull( hexstring, 0, 16 );
   return ( i >> lowbit ) & ( ( 1ull << numbits ) - 1 );
}

int main()
{
   cout << hex << bitRange( "F0FFC3", 7, 16 ) << '\n';
}
e1ff


For numbers 64 bit or less, I prefer 'bit twiddling'. Perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include <sstream>

int main() {
	constexpr const char* hex_ch_str { "F0FFC3" };

	const auto bin { std::strtoull(hex_ch_str, nullptr, 16)};
	const auto x { bin & 0x800000ull >> ((6 * 4) - 1)};
	const auto y { bin & 0x7full };
	const auto res { (bin & 0x7fff80ull) >> 7 };

	std::ostringstream oss;

	oss << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << res;
	std::cout << "x = " << x << ", y = " << y << ", extract = " << oss.str() << '\n';
}



x = 1, y = 67, extract = 0xE1FF

Last edited on
Work with strings. Extracting a field is then just extracting a substring; and the solution is more general, one that can handle strings of arbitrary length.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <iostream>
#include <bitset>
#include <string>
#include <cctype>
#include <random>

const char* hex_char_to_bin_str( char ch )
{
    switch(ch)
    {
        case '0': return "0000";
        case '1': return "0001";
        case '2': return "0010";
        case '3': return "0011";
        case '4': return "0100";
        case '5': return "0101";
        case '6': return "0110";
        case '7': return "0111";
        case '8': return "1000";
        case '9': return "1001";
        case 'A': case 'a' : return "1010";
        case 'B': case 'b' : return "1011";
        case 'C': case 'c' : return "1100";
        case 'D': case 'd' : return "1101";
        case 'E': case 'e' : return "1110";
        case 'F': case 'f' : return "1111";
        default: throw std::invalid_argument( "bad hex digit" ) ;
    }
}

std::string ltrimz( const std::string& str ) // trim leading zeroes
{
    std::size_t pos = 0 ;
    while( pos < str.size() && str[pos] == '0' ) ++pos ;
    return pos == str.size() ? "0" : str.substr(pos) ;
}

std::string hex_str_to_bin_str( std::string hex_str, bool trim = true )
{
    std::string bin_str ;
    for( char ch : hex_str ) bin_str += hex_char_to_bin_str(ch) ;
    return trim ? ltrimz(bin_str) : bin_str ;
}

std::string bin_str_to_hex_str( std::string bin_str, bool trim = true  )
{
    static const std::string hex_chars = "0123456789ABCDEF" ;

    std::string hex_str ;

    while( bin_str.size() % 4 ) bin_str = '0' + bin_str ; // pad to make size a multiple of 4
    for( std::size_t i = 0 ; i < bin_str.size() ; i += 4 ) hex_str += hex_chars[ std::stoul( bin_str.substr(i,4), nullptr, 2 ) ] ;

    return trim ? ltrimz(hex_str) : hex_str ;
}

std::string to_upper( const std::string& str )
{
    std::string ucase_str ;
    for( unsigned char c : str ) ucase_str += std::toupper(c) ;
    return ucase_str ;
}

std::string random_hex_str( std::size_t min_sz, std::size_t max_sz )
{
    static std::mt19937 rng( std::random_device{}() ) ;
    static std::uniform_int_distribution<std::size_t> sz_distrib ;
    static std::uniform_int_distribution<std::size_t> digit_distrib( 0, 15 ) ;
    static const std::string hex_chars = "0123456789ABCDEF" ;

    sz_distrib.param( decltype(sz_distrib)::param_type( min_sz, max_sz ) ) ;
    const auto sz = sz_distrib(rng) ;
    std::string hex_str ;
    while( hex_str.size() < sz ) hex_str += hex_chars[ digit_distrib(rng) ] ;
    return hex_str ;
}

int main() // test driver
{
    for( std::string hex_str : { "1234abcd", "0", "0fffffffffff1", "1234567890abcdef1234567890abcdef", "0000ffff" } )
    {
        const auto bin_str = hex_str_to_bin_str(hex_str) ;
        const auto hex_str2 = bin_str_to_hex_str(bin_str) ;
        std::cout << hex_str << ' ' << bin_str << ' ' << hex_str2 << ' '
                  << ( ltrimz(hex_str2) == ltrimz( to_upper(hex_str) ) ? "ok\n" : "*** error ***\n" ) ;
    }

    std::cout << '\n' ;

    for( std::string bin_str : { "010101010101010101", "0", "00000000111111111", "010000", "110110110110110" } )
    {
        const auto hex_str = bin_str_to_hex_str(bin_str) ;
        const auto bin_str2 = hex_str_to_bin_str(hex_str) ;
        std::cout << bin_str << ' ' << hex_str << ' ' << bin_str2 << ' '
                  << ( ltrimz(bin_str2) == ltrimz(bin_str ) ? "ok\n" : "*** error ***\n" ) ;
    }

    std::cout << '\n' ;

    for( std::size_t i = 5 ; i < 25 ; ++i )
    {
        const auto hex_str = random_hex_str( i*10, i*40 ) ;
        const auto bin_str = hex_str_to_bin_str( hex_str, false ) ;

        const auto hex_str2 = bin_str_to_hex_str(bin_str) ;
        const auto bin_str2 = hex_str_to_bin_str(hex_str) ;

        std::cout << "#digits: " << hex_str.size() << " hex, " << bin_str.size() << " bin - " ;
        if( ltrimz(hex_str2) == ltrimz( to_upper(hex_str) ) && ltrimz(bin_str2) == ltrimz(bin_str ) )
             std::cout << "ok\n" ;
        else std::cout << "*** error ***\n" ;
    }
}

http://coliru.stacked-crooked.com/a/7e5853d9e40b0e3d
Hello JLBorges, seeplus, lastchance, johnnin:

Wow.
Thank you all very much.
Thank you all soooo much.
I can never thank you enough.

You are all the best, brilliant and brightest experts of Modern C++.

Thank you, Thank you all for the most elegant C++ code gems you all have showed to me in this thread.

I am learning a lot from all of yours, gold of C++ code gems.

I need a some more help please.

I wrote program shown later below, based on couple of lines of excellent code written above by seeplus and below is a code fragment from it:

1
2
3
4
5
6
    std::ostringstream oss;

    oss << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << bitRange("F0FFC3", 7, 16);
    cout << oss.str() << endl;

    oss.str("");		oss.clear();


This code is using ostringstream object oss to format the hex string output values.

I need this output format of hex string values.

I am writing an embedded C++ program, that transmits text using serial communication protocol.

Cout, OSS etc are not allowed by my embedded software compiler.


May I seek your help in re-writing following two lines of code by using C++ string feature and without using streams, please?

1
2
    oss << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << bitRange("F0FFC3", 7, 16);
    cout << oss.str() << endl;


Above code gives following hex output:

0xE1FF




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <string>
#include <cstdlib>
#include <iomanip>
#include <sstream>

using namespace std;

unsigned long long bitRange(const string& hexstring, int lowbit, int numbits)
{
    unsigned long long i = stoull(hexstring, 0, 16);
    return (i >> lowbit) & ((1ull << numbits) - 1);
}

int main()
{
    cout << hex << bitRange("F0FFC3", 7, 16) << '\n';
    cout << hex << bitRange("8F0F0F0F0FC3", 7, 40) << '\n';

    std::ostringstream oss;

    oss << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << bitRange("F0FFC3", 7, 16);
    cout << oss.str() << endl;

    oss.str("");		oss.clear();

    oss << "0x" << std::hex << std::setw(4) << std::setfill('0') << std::uppercase << bitRange("8F0F0F0F0FC3", 7, 40);
    cout << oss.str() << endl;

    oss.str("");		oss.clear();
}


Above program is giving following output:


e1ff
1e1e1e1e1f
0xE1FF
0x1E1E1E1E1F


Last two lines of

Last edited on
try: printf("%X", bitrange(stuff);
may need the 0X in front of it ("0x%X").
or you can fiddle with cout to make it print like you want it. I am not sure that his stringstream is doing anything cout won't?
Last edited on
Each of your example programs are working very well on Windows laptop using Visual C++ 2022 compiler.

However, on microcontroller, and Arm Mbed C++ compiler, bitset using unsigned long long looks like will not work, is my guess.

In my program, binary strings are 48 bits and hex strings are 12 chars in length.

Because, of limitation of memory on the microcontroller, I need as small a program code size as possible.

Any implementation using C++ string may work.
I can't use any stream operators, because of serial communications.

I am looking at your programs to find a way to make this embedded C++ program work.

Finding of x, y values is working very well on embedded processor.

I need to make the code to extract 40 binary chars from 48 chars, after ignoring left most digit and right most seven digits, and regrouping to form Hex string of 10 ( 40/4) chars is the program functionality, that I need to make it work on embedded processor.

I am very thankful for your help.

Thanks and best regards,


Last edited on
meh, is this ok? Uses only string:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
  string hx ={"0123456789ABCDEF"};  //the hex digits, [value] = digit eg [15] = 'F'
  string s{"111100001111111111000011"}; //your binary string. 
  string s2 = "000"s + s.substr(1,16); //pad leading zeros because I am lazy.
  //cout << s2 << endl; //the desired substring with leading zeros, ok. 
  //cout << "12345678901234567890" << endl; //19 digits, correct (easy way to count the digits in output)
  string s3; //the result
  
  for(int i = s2.length()-1; i>4; i-=4) //gonna take 4 binary digits and spew out 1 hex digit now. 
  {	
    int dx = s2[i]=='1';
    dx += 2*(s2[i-1]=='1');	
	dx += 4*(s2[i-2]=='1');	
	dx += 8*(s2[i-3]=='1');	
	s3+=hx[dx];	
  }   
  cout << s3 << endl; 
}


yours works out so padding isnt useful, but if you decide to substring off like 13 bits, you would need it.
Last edited on
Hello jonnin,
Thank you very much for asking byte order.

I wrote following program and ran it on the Arm Nucleo F 429Zi processor board.

I kept break points on the two printf statements.

And the program stopped at following breakpoint statement:

 
        printf("Little Endian");


Following is the program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// byte_order.cpp

#include <iostream>
#include "mbed.h"

int main()
{
    union
    {
        uint32_t i;
        uint8_t c[4];
    } data;

    data.i = 0x01020304;

    if ( (data.c[0] == 0x01) && (data.c[3] == 0x04) )
    {
        printf("Big Endian. \n");
    }
    else if ( (data.c[0] == 0x04) && (data.c[3] == 0x01))
    {
        printf("Little Endian");
    }

    while (true) 
    {

    }
}




So I am assuming data is being stored like:


0x04030201



I have manually parsed couple of input Data Strings:




       4           3          2          1
76543210 98765432 10987654 32109876 54321098 76543210
10001111 00001111 00001111 00001111 00001111 11000011
8F 0F 0F 0F 0F c3
0001111 00001111 00001111 00001111 00001111 1
0001 1110 0001 1110 0001 1110 0001 1110 0001 1111
1E 1E 1E 1E 1F

       4           3          2          1
76543210 98765432 10987654 32109876 54321098 76543210
10001111 11111111 00000000 11111111 00110011 00111111
8F FF 00 FF 33 3F
0001111 11111111 00000000 11111111 00110011 0
0001 1111 1111 1110 0000 0001 1111 1110 0110 0110
1F FE 01 FE 66








Last edited on
Pages: 12