I have an input like
char *input="00112233FFAA";
uint8_t output[6];
What is the easiest way to convert input into output with sscanf? (prefer 1 line with no loop) The solution I have in mind doesn't scale to 20+ hex string.
sscanf(input, "%x%x%x%x%x",output[0], output[1]....output[5]);
Why scanf if this can be easily written by hand:
const size_t numdigits = strlen(input) / 2;
uint8_t * const output = malloc(numdigits);
for (size_t i = 0; i != numdigits; ++i)
{
output[i] = 16 * toInt(input[2*i]) + toInt(intput[2*i+1]);
}
unsigned int toInt(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return 10 + c - 'A';
if (c >= 'a' && c <= 'f') return 10 + c - 'a';
return -1;
}
If you do not want to use a loop, then you need to explicitly write out all six (or twenty) array locations (although %x is not the correct conversion character - it expects a pointer to unsigned int as its corresponding argument). If you don't want to write them all out, then you need to use a loop - it can be quite simple, though:
for (i = 0; i < 6; i++)
sscanf(&input[i * 2], "%2hhx", &output[i]);
Here is an alternate implementation.
#include <stdio.h>
#include <stdint.h>
#define _base(x) ((x >= '0' && x <= '9') ? '0' : \
(x >= 'a' && x <= 'f') ? 'a' - 10 : \
(x >= 'A' && x <= 'F') ? 'A' - 10 : \
'\255')
#define HEXOF(x) (x - _base(x))
int main() {
char input[] = "00112233FFAA";
char *p;
uint8_t *output;
if (!(sizeof(input) & 1)) { /* even digits + \0 */
fprintf(stderr,
"Cannot have odd number of characters in input: %d\n",
sizeof(input));
return -1;
}
output = malloc(sizeof(input) >> 1);
for (p = input; p && *p; p+=2 ) {
output[(p - input) >> 1] =
((HEXOF(*p)) << 4) + HEXOF(*(p+1));
}
return 0;
}
#caf already had a good simple idea.
However I had to use %02x and now it's working fine:
for (i = 0; i < 6; i++)
sscanf(&input[i * 2], "%02x", &output[i]);
Related
Hello I am trying to convert a hex value into the decimal form but for some reason the result I'm getting each time is negative and completely incorrect. Additionally I would like to take that decimal number and then convert it into it binary value. I have created func's for both but have run into the problem of "too few arguments" when calling my bin() func. If somebody could point me in the right direction and explain what I am doing wrong I would sincerely appreciate it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define arraysize 20
int decimal() {
int i = 0, val, len;
char hex1[arraysize];
long long dec = 0, base = 1;
len = strlen(hex1);
for (i = len--;i >= 0; i--) {
if (hex1[1] >= '0' && hex1[i] <= '9') {
dec += (hex1[i] - 48) * base;
base *= 16;
}
else if (hex1[i] >= 'A' && hex1[i] <= 'F') {
dec += (hex1[i] - 55) * base;
base *= 16;
}
else if (hex1[i] >= 'a' && hex1[i] <= 'f') {
dec += (hex1[i] - 87) * base;
base *= 16;
}
}
printf("Your decimal value is: %lld\n",dec);
return 0;
}
int bin(long long dec) {
int a[10], i;
for (i = 0; dec > 0; i++) {
a[i] = dec % 2;
dec = dec / 2;
}
printf("\nThe binary value is: ");
for (i = i - 1; i >= 0; i--) {
printf("%d", a[i]);
}
return 0;
}
int main() {
char hex1[arraysize];
printf("Enter your HEX value: ");
fflush(stdin);
fgets(hex1, arraysize, stdin);
decimal(hex1);
bin();
}
There are multiple problems in your code:
hex1 should be passed as an argument to decimal(). As posted, your code has undefined behavior because hex1 is an uninitialized local array.
i = len-- initializes i to the value of len, hence one position too far. Use i = len - 1 instead.
if (hex1[1] >= '0' && hex1[i] <= '9') uses hex[1] instead of hex[i]
you should use expressions with character constants '0', ('A' - 10) and ('a' - 10) instead of hard coded magical values 48, 55 and 87.
the array a is too short in function bin(). You should give it a length of at least 64.
the argument in bin() should have unsigned long long type.
fflush(stdin); has undefined behavior.
Here is a modified version:
#include <stdio.h>
unsigned long long decimal(const char *hex) {
unsigned long long dec = 0;
int i, c;
for (i = 0; (c = hex[i]) != '\0'; i++) {
if (c >= '0' && c <= '9') {
dec = dec * 16 + (c - '0');
} else
if (c >= 'A' && c <= 'F') {
dec = dec * 16 + (c - 'A' + 10);
} else
if (c >= 'a' && c <= 'f') {
dec = dec * 16 + (c - 'a' + 10);
}
}
return dec;
}
void bin(unsigned long long dec) {
char a[65];
int i;
a[64] = '\0';
for (i = 63;; i--) {
a[i] = '0' + dec % 2;
dec = dec / 2;
if (dec == 0)
break;
}
printf("The binary value is: %s\n", a);
}
int main() {
char hex1[20];
printf("Enter your HEX value: ");
if (fgets(hex1, sizeof hex1, stdin)) {
unsigned long long dec = decimal(hex1);
printf("Your decimal value is: %llu\n", dec);
bin(dec);
}
return 0;
}
The "too few arguments" error means exactly what it says: you are calling the bin() function with no arguments. In your function definition, you defined bin() as taking a long long dec argument. When you call bin(), you must give it an argument, like
bin(7);
You have made the opposite mistake in the line above:
decimal(hex1);
You defined decimal() as taking no arguments, yet you called it with a char[] argument. In your decimal function you can remove the line where you declare an array of chars, and instead make that an argument of the function:
int decimal(char* hex1) {
int i = 0, val, len;
long long dec = 0, base = 1;
. . .
You may want to further research the semantics of passing an array as an argument to a function.
Try this and replace char hex1[arraysize]="FFFFFFFFFFFFFFFF"; as needed:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define arraysize 20
#define bin_arraysize 64
unsigned long long decimal(char* hex1) {
long long dec = 0, base = 1;
int len = strlen(hex1);
for(int i = len-1;i >=0; i--) {
if(hex1[i] >= '0' && hex1[i] <= '9') {
dec += (hex1[i] - 48) * base;
base *= 16;
}
else if(hex1[i] >= 'A' && hex1[i] <= 'F') {
dec += (hex1[i] - 55) * base;
base *= 16;
}
else if(hex1[i] >= 'a' && hex1[i] <= 'f') {
dec += (hex1[i] - 87) * base;
base *= 16;
}
}
printf("Your decimal value is: %llu\n",dec);
return dec;
}
void bin(unsigned long long dec) {
int a[bin_arraysize], i;
for(i = 0; dec > 0; i++) {
a[i] = dec%2;
dec = dec/2;
}
printf("\nThe binary value is: ");
for(i = i - 1; i >= 0; i--) {
printf("%d", a[i]);
}
return;
}
int main() {
char hex1[arraysize]="FFFFFFFFFFFFFFFF";
unsigned long long dec=decimal(hex1);
bin(dec);
}
I need to scan the 32 hex number from command line and populated it into a uint8_t [16] array, I tried scanning the string and convert it to hex but its really a hassle since i cant find a function that does that, whats the best way to do this?
uint8_t get_int(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return 10 + c - 'A';
if (c >= 'a' && c <= 'f') return 10 + c - 'a';
return -1;
}
int main(void)
{
char buff[33];
size_t size;
size_t i;
uint8_t *output;
fgets(buff, sizeof buff, stdin);
size = strlen(buff) / 2;
output = malloc(size);
for (i= 0; i < size; ++i)
output[i] = get_int(buff[2*i]) * 16 + get_int(buff[2*i+1]);
for (i= 0; i < size; ++i)
printf("%u ", output[i]);
return 0;
}
ideone link
Hi this is my first question here so I apologize if I didn't follow all the rules for posting. This is K&R exercise 2-3 and I'm getting a segmentation fault when compiling with GCC and I'm not familiar with the debugger to understand what's going on. I'd be grateful if anyone could glance over the code and help me with what went wrong.
#include <stdio.h>
#define HEX 16
unsigned int htoi(char s[]) {
int i, len, n, rp, v;
v = 0;
if (s[0] == '0')
if (s[1] == 'x' || s[1] == 'X')
s[1] = '0';
for (len = 0; len != '\0'; ++len) {
}
for (i = len; i >= 0; --i) {
if (s[i] >= '0' && s[i] <= '9')
n = s[i] - '0';
else if (s[i] >= 'A' && s[i] <= 'F')
n = s[i] - 'A' + 10;
else if (s[i] >= 'a' && s[i] <= 'f')
n = s[i] - 'a' + 10;
rp = len - i;
v += n * HEX^rp;
}
return v;
}
int main() {
int test = htoi("0x1a9f");
printf("%d\n", test);
return 0;
}
You are passing the address of a string literal which is read-only. Doing the following will get rid of the segmentation fault.
char temp[] = "0x1a9f";
int test = htoi(temp);
Also:
v += n * HEX^rp;
Is ^ is the XOR operator and not the power operator. For power you need the pow function in math.h
Also:
for (i = len; i >= 0; --i) should be for (i = len - 1; i >= 0; --i) because the value of len goes out of the bound of the array. (Notified by #Grijesh Chauhan and #simonc)
int test = htoi("0x1a9f");
passes the string literal "0x1a9f" to htoi. This may exist in read-only memory and cannot be modified. You therefore get undefined behaviour (with a crash a valid example of this) when you try to write to the string in the line
s[1] = '0';
The easiest fix is to copy the original string into a modifiable variable
char s[] = "0x1a9f";
int test = htoi(s);
As reported by Grijesh, further into htoi, you also read beyond the bounds of the string
for (i = len; i >= 0; --i)
should be:
for (i = len - 1; i >= 0; --i)
I am writing this code to convert a hex entry into its integer equivalent. So A would be 10 and B would be 11 etc. This code acts weirdly, in that it seg. faults at random locations and including an extra newline character at times will get it to work. I am trying to debug it, just so I can understand what I am doing wrong here. Can anyone take a look and help me here ? Thanks a lot for your time.
/* Fixed working code for anyone interested */
#include <stdio.h>
#include <stdlib.h>
unsigned int hextoint(const char temp[])
{
int i;
int answer = 0;
int dec;
char hexchar[] = "aAbBcCdDeEfF" ;
for ( i=0; temp[i] != '\0'; i++ )
{
if ( temp[i] == '\0')
{
return ;
}
if (temp[i] == '0' || temp[i] == 'x' || temp[i] == 'X' )
{
printf("0");
answer = temp[i];
}
// compare each temp[i] with all contents in hexchar[]
int j;
int a = temp[i];
for ( j=0; hexchar[j] != '\0'; j++)
{
if ( temp[i] == hexchar[j] )
{
answer *= 16;
answer = answer + 10 + (j/2);
// printf("%d\n",answer );
break;
}
}
}
return answer;
}
main()
{
char *test[] =
{ "bad",
"aabbdd"
"0100",
"0x1",
"0XA",
"0X0C0BE",
"abcdef",
"123456",
"0x123456",
"deadbeef",
"zog_c"
};
int answer=0;
// Calculate the number of char's.
int numberOfChars;
numberOfChars = sizeof test /sizeof test[0];
printf("main():Number of chars = %d\n",numberOfChars);
int i;
// Go through each character and convert Hex to Integers.
for ( i = 0; i<numberOfChars;i++)
{
// Need to take the first char and then go through it and convert
it.
answer = hextoint(test[i]);
printf("%d\n",answer );
}
}
Let's take a look.
unsigned int hextoint(const char temp[])
{
int i;
int answer = 0;
char hexchar[] = "aAbBcCdDeEfF" ;
for ( i=0; temp[i] != '\0'; i++ )
{
printf("In here");
printf("%c\t",temp[i] );
}
return answer;
}
This doesn't seem to even try to do any conversion. It should always return 0, since answer is never assigned any other value. Normally, you'd do something like:
for (i=0; input[i] != '\0'; i++) {
answer *= 16;
answer += digit_value(input[i]);
}
return answer;
Where digit_value (obviously enough) returns the value of an individual digit. One way to do this is:
int digit_value(char input) {
input = tolower(input);
if (input >= '0' && input <= '9')
return input - '0';
if (input >= 'a' && input <= 'f')
return input - 'a' + 10;
return -1; // signal error.
}
Then, looking at main:
main()
{
Depending on the "implicit int" rule is generally poor practice, at least IMO. It's much better to specify the return type.
// Calculate the number of char's.
int numberOfChars;
numberOfChars = sizeof test /sizeof test[0];
This actually calculates the number of strings, not the number of chars.
for ( i = 0; i<=numberOfChars;i++)
Valid subscripts run from 0 through the number of items - 1, so this attempts to read past the end of the array (giving undefined behavior).
This'll work for any number within the unsigned int range, the nice thing is it does not use any other library functions so it is great for micro-controllers where space is tight.
unsigned int hexToInt(const char *hex)
{
unsigned int result = 0;
while (*hex)
{
if (*hex > 47 && *hex < 58)
result += (*hex - 48);
else if (*hex > 64 && *hex < 71)
result += (*hex - 55);
else if (*hex > 96 && *hex < 103)
result += (*hex - 87);
if (*++hex)
result <<= 4;
}
return result;
}
The problem is with calculating numberOfChars part. sizeof test is actually the size of the pointer, not the total length of all characters in your array, so the number returned in your code would be 1, which makes the for loop go to the second index of test (test[1]) which does not have a \0 at the end. Try using strlen for calculating numberOfChars.
This may not me the most optimal method, but it should work without problem.
unsigned int hex_to_int(const char* hex) {
unsigned int result = 0;
size_t len = strlen(hex);
for (size_t i = 0; i < len; ++i) {
char cur_char = tolower(hex[len - i - 1]);
// direct return if encounter any non-hex character.
if (!(isdigit(cur_char) && (cur_char >= 'a' && cur_char <= 'f'));)
return result;
unsigned int char_val = (isdigit(cur_char) ? cur_char - '0' : 10 + cur_char - 'a');
result += round(pow(16, i)) * char_val;
}
return result;
}
I need to parse strings of four hex characters to an integer. The characters appear inside a longer string, and there are no separators - I just know the offset they can be found in. The hex characters are case insensitive. Example with offset 3:
"foo10a4bar" -> 4260
I'm looking for a snippet that is
Short (too much code always creates complexity)
Simple (simple to understand and verify that it is correct)
Safe (invalid input is detected and signalled, no potential memory problems)
I'm a bit leery of using the 'sscanf' family of functions for this, but if there's a safe ANSI C solution using them, they can be used.
strtol is simple with good error handling:
const int OFFSET = 3, LEN = 4;
char hex[LEN + 1];
int i;
for(i = 0; i < LEN && str[OFFSET + i]; i++)
{
hex[i] = str[OFFSET + i];
if(!isxdigit((unsigned char) hex[i]))
{
// signal error, return
}
}
if(i != LEN)
{
// signal error, return
}
hex[LEN] = '\0';
char *end;
int result = (int) strtol(hex, &end, 16);
if(end != hex + LEN)
{
// signal error, return
}
It's usually best to use standard functions where you can, to get concise and simple code:
#define HEXLEN 4
long extract_hex(const char *src, size_t offset)
{
char hex[HEXLEN + 1] = { 0 };
long val;
if (strlen(src) < offset + HEXLEN)
return -1;
memcpy(hex, src + offset, HEXLEN);
if (strspn(hex, "0123456789AaBbCcDdEeFf") < HEXLEN)
return -1;
errno = 0;
val = strtol(hex, NULL, 16);
/* Out of range - can't occur unless HEXLEN > 7 */
if (errno)
return -1;
return val;
}
Here's my attempt
#include <assert.h>
static int h2d(char c) {
int x;
switch (c) {
default: x = -1; break; /* invalid hex digit */
case '0': x = 0; break;
case '1': x = 1; break;
case '2': x = 2; break;
/* ... */
case 'E': case 'e': x = 14; break;
case 'F': case 'f': x = 15; break;
}
return x;
}
int hex4(const char *src, int offset) {
int tmp, val = 0;
tmp = h2d(*(src+offset+0)); assert(tmp >= 0); val += tmp << 12;
tmp = h2d(*(src+offset+1)); assert(tmp >= 0); val += tmp << 8;
tmp = h2d(*(src+offset+2)); assert(tmp >= 0); val += tmp << 4;
tmp = h2d(*(src+offset+3)); assert(tmp >= 0); val += tmp;
return val;
}
Of course, instead of assert use your preferred method of validation!
And you can use it like this
int val = hex4("foo10a4bar", 3);
Here's an alternative based on character arithmetic:
int hexdigits(char *str, int ndigits)
{
int i;
int n = 0;
for (i=0; i<ndigits; ++i) {
int d = *str++ - '0';
if (d > 9 || d < 0)
d += '0' - 'A' + 10;
if (d > 15 || d < 0)
d += 'A' - 'a';
if (d > 15 || d < 0)
return -1;
n <<= 4;
n |= d;
}
return n;
}
It should handle digits in both cases, and work for both ASCII and EBCDIC. Using it for more than 7 digits invites integer overflow, and may make the use of -1 as an error value indistinguishable from a valid conversion.
Just call it with the offset added to the base string: e.g. w = hexdigits(buf+3, 4); for the suggested offset of 3 chars into a string stored in buf.
Edit: Here's a version with fewer conditions that is guaranteed to work for ASCII. I'm reasonably certain it will work for EBCDIC as well, but don't have any text of that flavor laying around to prove it.
Also, I fixed a stupid oversight and made the accumulator an int instead of unsigned short. It wouldn't affect the 4-digit case, but it made it overflow at only 16-bit numbers instead of the full capacity of an int.
int hexdigits2(char *str, int ndigits)
{
int i;
int n = 0;
for (i=0; i<ndigits; ++i) {
unsigned char d = *str++ - '0';
if (d > 9)
d += '0' - 'A' + 10;
if (d > 15)
d += 'A' - 'a';
if (d > 15)
return -1;
n <<= 4;
n |= d;
}
return n;
}
Usage is the same as the earlier version, but the generated code could be a bit smaller.
Here's my own try at it now that I thought about it for a moment - I'm not at all sure this is the best, so I will wait a while and then accept the answer that seems best to me.
val = 0;
for (i = 0; i < 4; i++) {
val <<= 4;
if (ptr[offset+i] >= '0' && ptr[offset+i] <= '9')
val += ptr[offset+i] - '0';
else if (ptr[offset+i] >= 'a' && ptr[offset+i] <= 'f')
val += (ptr[offset+i] - 'a') + 10;
else if (ptr[offset+i] >= 'A' && ptr[offset+i] <= 'F')
val += (ptr[offset+i] - 'A') + 10;
else {
/* signal error */
}
}
/* evaluates the first containing hexval in s */
int evalonehexFromStr( const char *s, unsigned long *val )
{
while( *s )
if( 1==sscanf(s++, "%04lx", val ) )
return 1;
return 0;
}
It works for exactly 4 hex-digits, eg:
unsigned long result;
if( evalonehexFromStr("foo10a4bar", &result) )
printf("\nOK - %lu", result);
If you need other hex-digit sizes, replace "4" to your size or take "%lx" for any hexval for values up to MAX_ULONG.
Code
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int offset = atoi(argv[2]);
argv[1][offset + 4] = '\0';
printf("%lu\n", strtol(argv[1] + offset, NULL, 0x10));
}
Usage
matt#stanley:$ make small_hex_converter
cc small_hex_converter.c -o small_hex_converter
matt#stanley:$ ./small_hex_converter f0010a4bar 3
4260