Есть ли какая-либо стандартная функция C, которая преобразует из шестнадцатеричной строки в массив байтов?
Я не хочу писать свою собственную функцию.
Шестнадцатеричная строка в байтовый массив в C
Ответ 1
Насколько я знаю, стандартная функция для этого не существует, но ее просто достичь следующим образом:
#include <stdio.h>
int main(int argc, char **argv) {
const char hexstring[] = "DEadbeef10203040b00b1e50", *pos = hexstring;
unsigned char val[12];
/* WARNING: no sanitization or error-checking whatsoever */
for (size_t count = 0; count < sizeof val/sizeof *val; count++) {
sscanf(pos, "%2hhx", &val[count]);
pos += 2;
}
printf("0x");
for(size_t count = 0; count < sizeof val/sizeof *val; count++)
printf("%02x", val[count]);
printf("\n");
return 0;
}
Изменить
Как указывал Al, в случае нечетного числа шестнадцатеричных цифр в строке, вы должны убедиться, что вы префикс его с началом 0. Например, строка "f00f5"
будет оцениваться как {0xf0, 0x0f, 0x05}
ошибочно в приведенном выше примере вместо правильного {0x0f, 0x00, 0xf5}
.
Немного изменил пример, чтобы ответить на комментарий @MassimoCallegari
Ответ 2
Я нашел этот вопрос по Google для того же самого. Мне не нравится идея вызова sscanf() или strtol(), поскольку он чувствует себя излишним. Я написал быструю функцию, которая не подтверждает, что текст действительно шестнадцатеричное представление потока байтов, но будет обрабатывать нечетное число шестнадцатеричных цифр:
uint8_t tallymarker_hextobin(const char * str, uint8_t * bytes, size_t blen)
{
uint8_t pos;
uint8_t idx0;
uint8_t idx1;
// mapping of ASCII characters to hex values
const uint8_t hashmap[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // !"#$%&'
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ()*+,-./
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // PQRSTUVW
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // XYZ[\]^_
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // `abcdefg
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hijklmno
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pqrstuvw
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xyz{|}~.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ........
};
bzero(bytes, blen);
for (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2)
{
idx0 = (uint8_t)str[pos+0];
idx1 = (uint8_t)str[pos+1];
bytes[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1];
};
return(0);
}
Ответ 3
Для коротких строк strtol
, strtoll
и strtoimax
будут работать очень хорошо (обратите внимание, что третий аргумент является базой для использования в обработке строки... установите ее на 16). Если ваш ввод длиннее number-of-bits-in-the-longest-integer-type/4
, тогда вам понадобится один из более гибких методов, предложенных другими ответами.
Ответ 4
Помимо превосходных ответов, приведенных выше, я хотел бы написать функцию C, которая не использует библиотеки и защищает от плохих строк.
uint8_t* datahex(char* string) {
if(string == NULL)
return NULL;
size_t slength = strlen(string);
if((slength % 2) != 0) // must be even
return NULL;
size_t dlength = slength / 2;
uint8_t* data = malloc(dlength);
memset(data, 0, dlength);
size_t index = 0;
while (index < slength) {
char c = string[index];
int value = 0;
if(c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = (10 + (c - 'A'));
else if (c >= 'a' && c <= 'f')
value = (10 + (c - 'a'));
else {
free(data);
return NULL;
}
data[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
return data;
}
Объяснение:
а. index/2 | Деление между целыми числами округляет значение, поэтому 0/2 = 0, 1/2 = 0, 2/2 = 1, 3/2 = 1, 4/2 = 2, 5/2 = 2 и т.д. Итак, для каждых 2 строковых символов мы добавляем значение в 1 байт данных.
б. (индекс + 1)% 2 | Мы хотим, чтобы нечетные числа приводили к 1 и даже к 0, поскольку первая цифра шестнадцатеричной строки является самой значимой и должна быть умножена на 16. Поэтому для индекса 0 => 0 + 1% 2 = 1, индекса 1 => 1 + 1% 2 = 0 и т.д.
с. & Л; & л; 4 | Сдвиг на 4 - это умножение на 16. пример: b00000001 & lt; & lt; 4 = b00010000
Ответ 5
По некоторой модификации формы user411313 код, следующий работает для меня:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main ()
{
char *hexstring = "deadbeef10203040b00b1e50";
int i;
unsigned int bytearray[12];
uint8_t str_len = strlen(hexstring);
for (i = 0; i < (str_len / 2); i++) {
sscanf(hexstring + 2*i, "%02x", &bytearray[i]);
printf("bytearray %d: %02x\n", i, bytearray[i]);
}
return 0;
}
Ответ 6
Сформированная версия сообщения Майкла Фукаракиса (так как у меня нет "репутации", чтобы добавить комментарий к этому сообщению):
#include <stdio.h>
#include <string.h>
void print(unsigned char *byte_array, int byte_array_size)
{
int i = 0;
printf("0x");
for(; i < byte_array_size; i++)
{
printf("%02x", byte_array[i]);
}
printf("\n");
}
int convert(const char *hex_str, unsigned char *byte_array, int byte_array_max)
{
int hex_str_len = strlen(hex_str);
int i = 0, j = 0;
// The output array size is half the hex_str length (rounded up)
int byte_array_size = (hex_str_len+1)/2;
if (byte_array_size > byte_array_max)
{
// Too big for the output array
return -1;
}
if (hex_str_len % 2 == 1)
{
// hex_str is an odd length, so assume an implicit "0" prefix
if (sscanf(&(hex_str[0]), "%1hhx", &(byte_array[0])) != 1)
{
return -1;
}
i = j = 1;
}
for (; i < hex_str_len; i+=2, j++)
{
if (sscanf(&(hex_str[i]), "%2hhx", &(byte_array[j])) != 1)
{
return -1;
}
}
return byte_array_size;
}
void main()
{
char *examples[] = { "", "5", "D", "5D", "5Df", "deadbeef10203040b00b1e50", "02invalid55" };
unsigned char byte_array[128];
int i = 0;
for (; i < sizeof(examples)/sizeof(char *); i++)
{
int size = convert(examples[i], byte_array, 128);
if (size < 0)
{
printf("Failed to convert '%s'\n", examples[i]);
}
else if (size == 0)
{
printf("Nothing to convert for '%s'\n", examples[i]);
}
else
{
print(byte_array, size);
}
}
}
Ответ 7
Здесь HexToBin и BinToHex относительно чисты и читаемы. (Примечание. Первоначально были возвращены коды ошибок enum через систему регистрации ошибок не простой -1 или -2.)
typedef unsigned char ByteData;
ByteData HexChar (char c)
{
if ('0' <= c && c <= '9') return (ByteData)(c - '0');
if ('A' <= c && c <= 'F') return (ByteData)(c - 'A' + 10);
if ('a' <= c && c <= 'f') return (ByteData)(c - 'a' + 10);
return (ByteData)(-1);
}
ssize_t HexToBin (const char* s, ByteData * buff, ssize_t length)
{
ssize_t result = 0;
if (!s || !buff || length <= 0) return -2;
while (*s)
{
ByteData nib1 = HexChar(*s++);
if ((signed)nib1 < 0) return -3;
ByteData nib2 = HexChar(*s++);
if ((signed)nib2 < 0) return -4;
ByteData bin = (nib1 << 4) + nib2;
if (length-- <= 0) return -5;
*buff++ = bin;
++result;
}
return result;
}
void BinToHex (const ByteData * buff, ssize_t length, char * output, ssize_t outLength)
{
char binHex[] = "0123456789ABCDEF";
if (!output || outLength < 4) return (void)(-6);
*output = '\0';
if (!buff || length <= 0 || outLength <= 2 * length)
{
memcpy(output, "ERR", 4);
return (void)(-7);
}
for (; length > 0; --length, outLength -= 2)
{
ByteData byte = *buff++;
*output++ = binHex[(byte >> 4) & 0x0F];
*output++ = binHex[byte & 0x0F];
}
if (outLength-- <= 0) return (void)(-8);
*output++ = '\0';
}
Ответ 8
hextools.h
#ifndef HEX_TOOLS_H
#define HEX_TOOLS_H
char *bin2hex(unsigned char*, int);
unsigned char *hex2bin(const char*);
#endif // HEX_TOOLS_H
hextools.c
#include <stdlib.h>
char *bin2hex(unsigned char *p, int len)
{
char *hex = malloc(((2*len) + 1));
char *r = hex;
while(len && p)
{
(*r) = ((*p) & 0xF0) >> 4;
(*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r));
r++;
(*r) = ((*p) & 0x0F);
(*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r));
r++;
p++;
len--;
}
*r = '\0';
return hex;
}
unsigned char *hex2bin(const char *str)
{
int len, h;
unsigned char *result, *err, *p, c;
err = malloc(1);
*err = 0;
if (!str)
return err;
if (!*str)
return err;
len = 0;
p = (unsigned char*) str;
while (*p++)
len++;
result = malloc((len/2)+1);
h = !(len%2) * 4;
p = result;
*p = 0;
c = *str;
while(c)
{
if(('0' <= c) && (c <= '9'))
*p += (c - '0') << h;
else if(('A' <= c) && (c <= 'F'))
*p += (c - 'A' + 10) << h;
else if(('a' <= c) && (c <= 'f'))
*p += (c - 'a' + 10) << h;
else
return err;
str++;
c = *str;
if (h)
h = 0;
else
{
h = 4;
p++;
*p = 0;
}
}
return result;
}
main.c
#include <stdio.h>
#include "hextools.h"
int main(void)
{
unsigned char s[] = { 0xa0, 0xf9, 0xc3, 0xde, 0x44 };
char *hex = bin2hex(s, sizeof s);
puts(hex);
unsigned char *bin;
bin = hex2bin(hex);
puts(bin2hex(bin, 5));
size_t k;
for(k=0; k<5; k++)
printf("%02X", bin[k]);
putchar('\n');
return 0;
}
Ответ 9
Ниже приводится решение, которое я написал по следующим причинам:
void hex2bin(const char* in, size_t len, unsigned char* out) {
static const unsigned char TBL[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59, 60, 61,
62, 63, 64, 10, 11, 12, 13, 14, 15, 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, 10, 11, 12, 13, 14, 15
};
static const unsigned char *LOOKUP = TBL - 48;
const char* end = in + len;
while(in < end) *(out++) = LOOKUP[*(in++)] << 4 | LOOKUP[*(in++)];
}
Пример:
unsigned char seckey[32];
hex2bin("351aaaec0070d13d350afae2bc43b68c7e590268889869dde489f2f7988f3fee", 64, seckey);
/*
seckey = {
53, 26, 170, 236, 0, 112, 209, 61, 53, 10, 250, 226, 188, 67, 182, 140,
126, 89, 2, 104, 136, 152, 105, 221, 228, 137, 242, 247, 152, 143, 63, 238
};
*/
Если вам не нужно поддерживать строчные буквы:
static const unsigned char TBL[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59,
60, 61, 62, 63, 64, 10, 11, 12, 13, 14, 15
};
Ответ 10
char *hexstring = "deadbeef10203040b00b1e50", *pos = hexstring;
unsigned char val[12];
while( *pos )
{
if( !((pos-hexstring)&1) )
sscanf(pos,"%02x",&val[(pos-hexstring)>>1]);
++pos;
}
sizeof (val)/sizeof (val [0]) является избыточным!
Ответ 11
In main()
{
printf("enter string :\n");
fgets(buf, 200, stdin);
unsigned char str_len = strlen(buf);
k=0;
unsigned char bytearray[100];
for(j=0;j<str_len-1;j=j+2)
{ bytearray[k++]=converttohex(&buffer[j]);
printf(" %02X",bytearray[k-1]);
}
}
Use this
int converttohex(char * val)
{
unsigned char temp = toupper(*val);
unsigned char fin=0;
if(temp>64)
temp=10+(temp-65);
else
temp=temp-48;
fin=(temp<<4)&0xf0;
temp = toupper(*(val+1));
if(temp>64)
temp=10+(temp-65);
else
temp=temp-48;
fin=fin|(temp & 0x0f);
return fin;
}
Ответ 12
Это модифицированная функция из аналогичного вопроса, измененная в соответствии с предложением fooobar.com/questions/126225/....
Эта функция преобразует шестнадцатеричную строку - НЕ добавляется с "0x" - с четным числом символов до количества указанных байтов. Он будет возвращать -1, если он встречает недопустимый символ, или если шестнадцатеричная строка имеет нечетную длину и 0 при успехе.
//convert hexstring to len bytes of data
//returns 0 on success, -1 on error
//data is a buffer of at least len bytes
//hexstring is upper or lower case hexadecimal, NOT prepended with "0x"
int hex2data(unsigned char *data, const unsigned char *hexstring, unsigned int len)
{
unsigned const char *pos = hexstring;
char *endptr;
size_t count = 0;
if ((hexstring[0] == '\0') || (strlen(hexstring) % 2)) {
//hexstring contains no data
//or hexstring has an odd length
return -1;
}
for(count = 0; count < len; count++) {
char buf[5] = {'0', 'x', pos[0], pos[1], 0};
data[count] = strtol(buf, &endptr, 0);
pos += 2 * sizeof(char);
if (endptr[0] != '\0') {
//non-hexadecimal character encountered
return -1;
}
}
return 0;
}
Ответ 13
Нет. Но это относительно тривиально для достижения с помощью sscanf
в цикле.
Ответ 14
Попробуйте использовать следующий код:
static unsigned char ascii2byte(char *val)
{
unsigned char temp = *val;
if(temp > 0x60) temp -= 39; // convert chars a-f
temp -= 48; // convert chars 0-9
temp *= 16;
temp += *(val+1);
if(*(val+1) > 0x60) temp -= 39; // convert chars a-f
temp -= 48; // convert chars 0-9
return temp;
}
Ответ 15
Здесь моя версия:
/* Convert a hex char digit to its integer value. */
int hexDigitToInt(char digit) {
digit = tolower(digit);
if ('0' <= digit && digit <= '9') //if it decimal
return (int)(digit - '0');
else if ('a' <= digit && digit <= 'f') //if it abcdef
return (int)(digit - ('a' - 10));
else
return -1; //value not in [0-9][a-f] range
}
/* Decode a hex string. */
char *decodeHexString(const char *hexStr) {
char* decoded = malloc(strlen(hexStr)/2+1);
char* hexStrPtr = (char *)hexStr;
char* decodedPtr = decoded;
while (*hexStrPtr != '\0') { /* Step through hexStr, two chars at a time. */
*decodedPtr = 16 * hexDigitToInt(*hexStrPtr) + hexDigitToInt(*(hexStrPtr+1));
hexStrPtr += 2;
decodedPtr++;
}
*decodedPtr = '\0'; /* final null char */
return decoded;
}
Ответ 16
Может быть, это проще?
uint8_t hex(char ch) {
uint8_t r = (ch > 57) ? (ch - 55) : (ch - 48);
return r & 0x0F;
}
int to_byte_array(const char *in, size_t in_size, uint8_t *out) {
int count = 0;
if (in_size % 2) {
while (*in && out) {
*out = hex(*in++);
if (!*in)
return count;
*out = (*out << 4) | hex(*in++);
*out++;
count++;
}
return count;
} else {
while (*in && out) {
*out++ = (hex(*in++) << 4) | hex(*in++);
count++;
}
return count;
}
}
int main() {
char hex_in[] = "deadbeef10203040b00b1e50";
uint8_t out[32];
int res = to_byte_array(hex_in, sizeof(hex_in) - 1, out);
for (size_t i = 0; i < res; i++)
printf("%02x ", out[i]);
printf("\n");
system("pause");
return 0;
}
Ответ 17
Лучший способ, которым я знаю:
int hex2bin_by_zibri(char *source_str, char *dest_buffer)
{
char *line = source_str;
char *data = line;
int offset;
int read_byte;
int data_len = 0;
while (sscanf(data, " %02x%n", &read_byte, &offset) == 1) {
dest_buffer[data_len++] = read_byte;
data += offset;
}
return data_len;
}
Функция возвращает количество преобразованных байтов, сохраненных в dest_buffer. Входная строка может содержать пробелы и буквы в смешанном регистре.
"01 02 03 04 ab Cd eF garbage AB"
переводит в dest_buffer, содержащий 01 02 03 04 ab cd ef
а также "01020304abCdeFgarbageAB"
переводит как раньше.
Разбор останавливается при первой "ошибке".
Ответ 18
Моя версия с использованием побитовых операций:
char hex_to_byte(char hex[2])
{
char result = 0;
if (hex[1] & 0b01000000)
result |= hex[1] + 0b1001; //a-f
else
result |= hex[1]; //0-9
result &= 0b00001111;
if (hex[0] & 0b01000000) //a-f
result |= (hex[0] << 4) + 0b1001;
else //0-9
result |= hex[0] << 4;
return result;
}
Тестовое задание:
#include <unistd.h>
char test[26] = "48656c6c6f20776f726c64210a"; //Hello world!\n
char result[13];
for (int i = 0; i < 13; i++)
result[i] = hex_to_byte(&test[i*2]);
write(1, result, 13);
Из:
Hello world!