I have an unsigned char array which contains hex bytes like below:
unsigned char array[255];
array[0] = 'F';
array[1] = 'F';
array[2] = 'E';
array[3] = '2';
array[4] = 'A';
array[5] = 'A';
array[6] = 'C';
array[7] = 'C';
I want to merge them so that it becomes:
array[0] = "FF"
array[1] = "E2"
array[2] = "AA"
array[3] = "CC"
array[0] = '\xFF';
array[1] = '\xE2';
array[2] = '\xAA';
array[3] = '\xCC';
I have tried using sprintf but then I do not know how to specify index number in it. Any help.?
So, you want to convert your string, made of hexadecimal characters, into an array of bytes, right? Know your data.
sprintf() will not help you, since it produces strings. Instead, you will need to extract the 'value' of each hexa character and use it to calculate the value of your bytes.
So, let's create a helper function to convert a hexadecimal character to its integer value (or -1 if it is invalid). We will use the characters' ASCII values and the fact that character ranges are contiguous in the ASCII table
int char2hexa(unsigned char c)
{
if(c >= '0' && c <= '9') {
return (c - '0'); /* will return 0-9 */
} else if(c >= 'A' && c <= 'F') {
return (c - 'A') + 10; /* will return 10-15 */
} else if(c >= 'a' && c <= 'f') {
return (c - 'a') + 10; /* will return 10-15 */
} else {
return -1;
}
}
Now a byte will be constructed from two hexa values by using one as the upper nibble (multiplied by 16 or shifted left by 4) and the other as a lower nibble, so let's have a function for that:
unsigned char hexvals2byte(int upper, int lower)
{
return (upper * 16 + lower);
}
and put the pieces together. I will assume that:
you know the length of your input data
the length is even (you need two characters per byte)
you want to put the result in the same array
Here comes.
#include <stdio.h>
#include <stdlib.h>
unsigned char array[255];
array[0] = 'F';
array[1] = 'F';
array[2] = 'E';
array[3] = '2';
array[4] = 'A';
array[5] = 'A';
array[6] = 'C';
array[7] = 'C';
unsigned length = 8;
int upper, lower;
for(int i = 0; i < length; i+=2) {
upper = char2hexa(array[i]);
lower = char2hexa(array[i+1]);
if(upper < 0 || lower < 0) {
/* handle input data format error */
fprintf(stderr, "ERROR: Cannot decode hexa values '%c%c'\n", array[i], array[i+1]);
exit(EXIT_FAILURE);
}
array[i/2] = hexvals2byte(upper, lower);
}
So you need a result array of unsigned char result[128][3], then assign the part results, grouping 2 source elements into one result sub-element:
unsigned char result[128][3] = { 0 };
int i;
for (i = 0; i < 255; ++i)
{
result[i/2][i%2] = array[i];
}
The reason for size 3 is, that you need 2 characters and one zero-delimiter to form a string.
An easy way to convert a digit to number is to subtract '0' from it:
char digit = '3';
int number = digit - '0'; /* number = 3 */
This works only for digits (digit >= '0' && digit <= '9'), for hexadecimal digits ('A', 'B', etc.) you have to do a little more job:
unsigned char result[127];
int i;
unsigned char current;
unsigned char calc_diff(unsigned char digit) {
if(digit >= '0' && digit <= '9')
return '0';
else if(digit >= 'A' && digit <= 'F')
return 'A' - 10;
else if(digit >= 'a' && digit <= 'f')
return 'a' - 10;
else
return 0; // handle invalid digit
}
for(i = 0; i < 128; ++i) {
current = array[2 * i];
result[i] = (current - calc_diff(current)) << 4;
current = array[(2 * i) + 1];
result[i] |= current - calc_diff(current);
}
You want to convert characters to their hexadecimal value and combine them in pairs.
Here is a simple program to illustrate how you can do this:
#include <stdio.h>
#include <string.h>
static int xdigit(unsigned char c) {
/* this method is inefficient but works for all encodings */
static const char xdigits[] = "abcdef0123456789ABCDEF";
const char *p = memchr(xdigits, c, 22);
return p ? (p - xdigits + 10) & 15 : -1;
}
int main(void) {
unsigned char array[255];
while (scanf("%254s", array) == 1) {
int i, j, d, d2 = 0;
for (i = j = 0; array[i] != '\0'; i++) {
d = xdigit(array[i]);
if (d < 0) {
printf("invalid hexadecimal digit: %c\n", array[i]);
break;
}
d2 = (d2 << 4) | d;
if (i & 1) {
array[j++] = (unsigned char)d2;
d2 = 0;
}
}
array[j] = '\0';
printf("converted array: %s\n", array);
}
return 0;
}
Here is a more elaborate version, with an separate conversion function and more explicit output:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static int xdigit(unsigned char c) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return c - '0';
case 'A': case 'a':
return 10;
case 'B': case 'b':
return 11;
case 'C': case 'c':
return 12;
case 'D': case 'd':
return 13;
case 'E': case 'e':
return 14;
case 'F': case 'f':
return 15;
default:
return -1;
}
}
int xconvert(unsigned char *dest, const unsigned char *src, int len) {
int i, j, d, d2 = 0;
for (i = j = 0; i < len; i++) {
d = xdigit(src[i]);
if (d < 0) {
printf("invalid hexadecimal digit: %c\n", src[i]);
return -1;
}
d2 = (d2 << 4) | d;
if (i & 1) {
dest[j++] = (unsigned char)d2;
d2 = 0;
}
}
if (i & 1) {
printf("missing trailing digit\n");
return -1;
}
return j;
}
int main(void) {
unsigned char array[255];
int i, len, c;
while (scanf("%254s", array) == 1) {
len = xconvert(array, array, strlen((char *)array));
if (len >= 0) {
printf("converted array: \"");
for (i = 0; i < len; i++) {
c = array[i];
if (isprint(c)) {
putchar(c);
} else {
printf("\\x%02X", c);
}
}
printf("\"\n");
}
}
return 0;
}
my stab at it. Here you have to know the size of the array. in your case 255
//counters and array's
int first = 0;
int second = 0;
int count = 0;
char foo[8] = {'F', 'F', 'E', '2', 'A', 'A', 'C', 'C'};
//array half the size of the first one.
char *done[4];
//loop through first array
while (first <= 7)
{
//if its the first letter
if (second == 0)
{
//allocate enough mem to second arr
done[count] = (char *)malloc(sizeof(char *) * 3);
//assaign the first letter
done[count][0] = foo[first];
//indicate the next step for the second letter
second = 1;
}
//if its the second letter
else if (second == 1)
{
//assign second letter
done[count][1] = foo[first];
//null the string
done[count][2] = '\0';
//increase posistion index for the second arr
count++;
//indicate nexxt step is a the first letter of the next step
second = 0;
}
//increment the index for the first arr
first++;
}
Given that the data is in ASCII format and you want to merge it into a raw binary format, then:
for(size_t i=0; i<n; i++)
{
array[i] = to_int(array[i]);
}
where to_int() is your custom routine for converting from hexadecimal ASCII to integer. That is, if digit subtract with '0', else if upper-case letter subtract with 'A' and add 0xA.
Then after that, merge the items:
for(size_t i=0; i<n; i+=2)
{
array[i] = (unsigned int)array[i]<<4 | array[i+1];
}
I suppose you mix up an 8 bit value (e.g. 0x0F, which is 15 in decimal) with a character value like 'F' (which corresponds to 70 in ASCII format) with a string literal "FF" (which corresponds to a pointer to a sequence of the three character values {'F','F','\0'}.
From the context you present it seems that you mean 8 bit values, which are represented by data type unsigned char.
Given that, the code could look as follows:
unsigned char array[255] = { 0xF,0xE,0x2,0xA,0xA,0xC,0xC };
int target=0;
for (int i=0; i<254; i+=2) {
array[target] = (array[i] << 4) + array[i+1];
target++;
}
Maybe there are uncertain things but perhaps you want to do this.
#include <stdio.h>
int main(void){
unsigned char array[255] = {//or char -> hex byte
'\xF', '\xF', '\xE', '\x2',
'\xA', '\xA', '\xC', '\xC',
};
int len = 8;
for(int i = 0, j = 0; i < len; i += 2){
array[j++] = (array[i] << 4) | array[i+1];
}
len = len / 2;
for(int i = 0; i < len; i++){
printf("%02hhX", array[i]);//FFE2AACC
}
printf("\n");
}
When the data held first is a hex character
#include <stdio.h>
#include <ctype.h>
int main(void){
unsigned char array[255] = {
'F', 'F', 'E', '2',
'A', 'A', 'C', 'C',
};
int len = 8;
for(int i = 0, j = 0; i < len; i++){
if(isxdigit(array[i])){//maybe redundant
if(isdigit(array[i])){
array[i] -= '0';
} else {
array[i] -= isupper(array[i]) ? 'A' : 'a';
array[i] += 10;
}
} else {
fprintf(stderr, "invalid data %c\n", array[i]);
return -1;
}
}
for(int i = 0, j = 0; i < len; i += 2){
array[j++] = (array[i] << 4) | array[i+1];
}
len = len / 2;
for(int i = 0; i < len; i++){
printf("%02hhX", array[i]);
}
printf("\n");
}
Related
i have an uint8_t Variable which contains a substring of 4 hexadecimal variables. Example:
uint8_t String[10] = "00AABBCC";
I would like to take these 4 hex Variables into different hex values:
uint8_t Data_Byte[4];
Data_Byte[0]=0x00;
Data_Byte[1]=0xAA;
Data_Byte[2]=0xBB;
Data_Byte[3]=0xCC;
How can I take these 4 substrings into 4 different uint8_t Variables?
You can use sscanf to parse each two-character pair in the string into a number:
uint8_t arr[strlen(String) / 2];
for (int i = 0; i < strlen(String); i += 2) {
sscanf(String + i, "%2hhx", &arr[i / 2]);
}
If you're developing on a system with limited sscanf support, you can use something like this:
for (int i = 0; i < strlen(String); i += 2) {
uint8_t val1 = isdigit(String[i]) ? (String[i] - '0') : (String[i] - 'A' + 10);
uint8_t val2 = isdigit(String[i + 1]) ? (String[i + 1] - '0') : (String[i + 1] - 'A' + 10);
arr[i / 2] = val1 << 4 | val2;
}
With your stipulation the strings will represent 4 bytes, this a far-easier-to-read-and-understand solution IMO. I have no comment on efficiency.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <arpa/inet.h>
bool convert(const uint8_t* strValue, uint8_t* cvrtArray)
{
// make 2nd parameter non-NULL for better error checking
errno = 0;
char* endptr = NULL;
// convert to unsigned long
unsigned long val = strtoul((const char*)strValue, &endptr, 16);
// do some error checking, this probably needs some improvements
if (errno == ERANGE && val == ULONG_MAX)
{
fprintf(stderr, "Overflow\n");
return false;
}
else if ((strValue != NULL) && (*endptr != '\0'))
{
fprintf(stderr, "Cannot convert\n");
return false;
}
// potential need to flip the bytes (your string is big endian, and the
// test machine on godbolt is little endian)
val = htonl(val);
// copy to our array
memcpy(cvrtArray, &val, 4);
return true;
}
int main(void)
{
uint8_t Data_Byte[4] = { 0 };
uint8_t String[10] = "00AABBCC";
if (convert(String, Data_Byte) == true)
{
for(size_t i=0; i<sizeof Data_Byte; i++)
{
printf("Data_Byte[%zu] = 0x%02" PRIX8 "\n", i, Data_Byte[i]);
}
}
else
{
fprintf(stderr, "There was a problem converting %s to byte array\n", String);
}
return 0;
}
code in action
I took some inspiration from 0___________ and made my own:
static char digits[] = "0123456789ABCDEF";
void convert(uint8_t *chrs, uint8_t *buff)
{
size_t len = strlen((char *)chrs);
size_t i;
for(i = 0; i < len; i+=2) {
buff[i / 2] = (strchr(digits, chrs[i]) - digits);
buff[i / 2] += (strchr(digits, chrs[i+1]) - digits) << 4;
}
if(i<len)
buff[i / 2] = (strchr(digits, chrs[i]) - digits);
}
The changes are that I find it much more natural to do a complete element in every iteration. To account for odd length input strings, I just added an if statement in the end that takes care of it. This can be removed if input strings always have even length. And I skipped returning the buffer for simplicity. However, as 0___________ pointed out in comments, there are good reasons to return a pointer to the output buffer. Read about those reasons here: c++ memcpy return value
static char digits[] = "0123456789ABCDEF";
uint8_t *convert(uint8_t *chrs, uint8_t *buff)
{
size_t len = strlen((char *)chrs);
for(size_t i = 0; i < len; i++)
{
int is_first_digit = !(i & 1);
int shift = is_first_digit << 2;
buff[i / 2] += (strchr(digits, chrs[i]) - digits) << shift;
}
return buff;
}
int main(void)
{
uint8_t String[] = "00AABBCC";
uint8_t buff[4];
convert(String, buff);
for(size_t i = 0; i < sizeof(buff); i++)
{
printf("%hhx", buff[i]); // I know it is wrong format
}
}
https://godbolt.org/z/9c8aexTvq
Or even faster solution:
int getDigit(uint8_t ch)
{
switch(ch)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return ch - '0';
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return ch - 'A' + 10;
}
return 0;
}
uint8_t *convert(uint8_t *chrs, uint8_t *buff)
{
size_t len = strlen((char *)chrs);
for(size_t i = 0; i < len; i++)
{
int is_first_digit = !(i & 1);
int shift = is_first_digit << 2;
buff[i / 2] += (getDigit(chrs[i])) << shift;
}
return buff;
}
Remember: use functions for this kind of tasks. Do not program in main.
I have a 40 digit hex number stored in a string, I have to store it inside a struct called Int40 that only contains a pointer to an int.
typedef struct Int40
{
// a dynamically allocated array to hold a 40
// digit integer, stored in reverse order
int *digits;
} Int40;
this is what I have tried
Int40 *parseString(char *str)
{
Int40 *value = malloc(sizeof(Int40) * MAX40);
for (int i = 0; i < MAX40; i++)
{
value[i] = (int)str[i];
}
return value;
}
int main()
{
Int40 *p;
p = parseString("0123456789abcdef0123456789abcdef01234567");
printf("-> %d\n", *p);
}
I know that an Int cant contain 40 digits thats why I tried to store each number from the string in an array of integers but my code doesnt seem to work.
Edit: Also the number contains letters because is a hex number, will I have to the get the ascii value of the hex number to be able to store it in the array of int, how do i do that?
You might do something like the following (note that I omit verification of the argument char* and also assume that hex characters are lowercase)
// with if statements:
Int40 *parseString(char *str)
{
Int40 *value = malloc(sizeof(Int40) * MAX40);
// save the digits array locally (same memory address as value)
int* digits = value->digits;
for (int i = 0; i < MAX40; i++)
{
char c = str[i];
// decimal digits case
if (c >= '0' && c <= '9') {
digits[i] = c - '0'; // subtract '0' to get the numberical value of c
} else { // hex case
digits[i] = (c - 'a') + 10; // subtract 'a' to get the numerical value of c as 0 + 10 for hex characters A - F
}
}
return value;
}
An alternative:
// with a switch statements:
Int40 *parseString(char *str)
{
Int40 *value = malloc(sizeof(Int40) * MAX40);
// save the digits array locally (same memory address as value)
int* digits = value->digits;
for (int i = 0; i < MAX40; i++)
{
char c = str[i];
switch (c) {
// hex 10 - 15
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
digits[i] = (c - 'a') + 10;
break;
// hex 0 - 9
default:
digits[i] = c - '0';
}
}
return value;
}
#include <stdio.h>
#include <stdlib.h>
typedef int* hex;
hex parseString(char *str)
{
hex value = (hex)malloc(sizeof(int)*40);
for (int i = 0; i < 40; i++)
{
value[i] = str[i];
}
return value;
}
int main()
{
hex p;
p = parseString("0123456789abcdef0123456789abcdef01234567");
printf("-> %d\n", p[0]);
}
...
#include <stdio.h>
#include <stdlib.h>
typedef struct Int40
{
int* hex;
}Int40;
Int40 parseString(char *str)
{
Int40 value;
value.hex = (int*)malloc(sizeof(int)*40);
for (int i = 0; i < 40; i++)
{
value.hex[i] = str[i];
}
return value;
}
int main()
{
Int40 p;
p = parseString("0123456789abcdef0123456789abcdef01234567");
printf("-> %d\n", p.hex[0]);
}
I am trying to convert ASCII hexadecimal value (0x61 or 61) to it's char value (61 = a, etc..) without atoi, scanf, strcat.
I am saving getchar() to temp variable, then I save first one (6) in array, then save second one (1) in array, now i want take these two and convert them to their ASCII value.
68656c6c6f0d0a = hello
void asd(char fde)
{
char asmg[3];
asmg[0] = "0x";
asmg[1] = "6";
asmg[2] = "1";
printf("%c", asmg);
}
I expected it to print "a", but it doesn't work.
Something like that, but this doesn't work. I need to put asmg[0], [1], [2] to one char, then it should work.
Thanks :)
Not sure exactly what you are trying to do but this may help.
int main(int argc, char **argv)
{
int hex[6] = {0x68, 0x65, 0x6C, 0x6C, 0x6F};
int i = 0;
for(i = 0; i <6; i++)
{
printf("%c", hex[i]);
}
printf("\n");
}
#include <stdio.h>
int main(void){
char hex[] = "68656c6c6f0d0a";
char text[(sizeof(hex)+1)/2];
int i = 0, j = 0;
while(hex[i]){
int up = '0' <= hex[i] && hex[i] <= '9' ? hex[i] - '0' : hex[i] - 'a' + 10;//lowcase
if(hex[++i] == '\0'){
printf("invalid format\n");
return -1;
}
int low = '0' <= hex[i] && hex[i] <= '9' ? hex[i] - '0' : hex[i] - 'a' + 10;//lowcase
text[j++] = up * 16 + low;
++i;
}
text[j] = 0;
printf("%s", text);
return 0;
}
you can use this function to convert Hexastring to ASCII
void hexToAscii(unsigned char *buf, int size, char *str) {
int i;
for (i = 0; i < size; ++i) {
sprintf(str + (i * 2), "%02X", buf[i]);
}
}
Don't for get to set the last element on the string to null.
For example,
char str[20];
str="ABCD";
Output :
1 - A, B, C,D
2 - AB,AC, AD BC, BD, CD.
3 - ABC, ABD, BCD.
4 - ABCD.
The sub-sequences are generated only in left to right fashion. Thanks :)
#include<stdio.h>
#include <conio.h>
#include<string.h>
int sub[10];
void next(int max, int length) {
int pos = length - 1,a;
//find first digit that can be increased
while(pos >= 0)
{
if(sub[pos] == max - (length - 1 - pos))
pos--;
else
break;
}
sub[pos]++; //increase digit
//update other digits
for( a = pos+1; a < length; a++)
sub[a] = sub[a-1] + 1;
}
int main()
{
char word[100];
printf("Enter a word = ");
scanf("%s",word);
int max = strlen(word) - 1,n,i,a,b;
for(n=1; n <= max+1; n++)
{
printf("\n");
for(i = 0; i < n; i++)
{
sub[i] = i;
}
for(a = 0; ; a++)
{
for(b=0; b < max+1; b++)
printf("%c",word[sub[b]]);
printf("\n");
if(sub[0] == max - (n - 1))
break;
else
next(max, n);
}
printf("\n");
}
return 0;
}
This code only produces sub-sequences of the length equal to the length of the string and it also repeats a particular character.
not the cleanest, most effective code, but this'll do :D
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
char *printForMask(int mask, char *str)
{
int i = 0;
int j = 0;
char *maskedString = (char *)malloc(sizeof(char) * strlen(str) + 1);
for (; mask > 0; mask >>= 1, j++) {
if (mask & 1)
maskedString[i++] = str[j];
}
maskedString[i] = '\0';
return maskedString;
}
int main()
{
char str[20] = "ABCD";
int mask = 1;
int maskLen = 1;
int stringLen = strlen(str);
for (maskLen = 1; maskLen <= stringLen; maskLen++) {
printf("%d ", maskLen);
for (mask = (int)pow(2,maskLen) - 1; mask <= (int) pow(2, stringLen) - 1; mask <<= 1) {
char *temp = printForMask(mask, str);
printf ("%s%s", temp, (mask << 1 <= (int) pow(2, stringLen) - 1) ? ", " : "\n");
free(temp);
}
}
return 0;
}
EDIT
for the string ABCD
str[0] = 'A'
str[1] = 'B'
str[2] = 'C'
str[3] = 'D'
right?
So What I'm doing is using the binary representation of a integer 2^strlen(str) - 1
which in this case would be 2^4 - 1 = 15 = 0b1111
In the first for-loop of the main function, I increase the mask, meaning
start off with a mask = 0b0001, for each iteration of the loop, increase the mask to mask = 0b1111
And in the inner for loop, I shift the mask so that something like this happens
mask = 0b0001 //A
mask = 0b0010 //B
mask = 0b0100 //C
mask = 0b1000 //D
//The inner loop will finish here, and the mask length will become 2
mask = 0b0011 //AB
mask = 0b0110 //BC
mask = 0b1100 //CD
//etc. The string representation is made by the function printForMask()
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