Как преобразовать шестнадцатеричную строку в массив uint8_t? (Язык C)

#c #type-conversion #hex #uint8t #uint8array

Вопрос:

как я могу преобразовать шестнадцатеричную строку в массив uint8_t? Моя строка есть 02012B1530A6E3958A98530031902003876940000000000CDF9844173BE512AFFFFFFE11DBBA1F00079387800E13012E11FC017FFFFFFFFE39C10F40 , и я хочу преобразовать ее в этот массив:

 uint8_t array_uint[] = {0x02, 0x01, 0x2B, 0x15, 0x30, 0xA6, 0xE3, 0x95, 0x8A, 0x98, 0x53, 0x00, 0x31, 0x90, 0x20, 0x03, 0x87, 0x69, 0x40, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xDF, 0x98, 0x44, 0x17, 0x3B, 0xE5, 0x12, 0xAF, 0xFF, 0xFF, 0xFE, 0x11, 0xDB, 0xBA, 0x1F, 0x00, 0x07, 0x93, 0x87, 0x80, 0x0E, 0x13, 0x01, 0x2E, 0x11, 0xFC, 0x01, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0x39, 0xC1, 0x0F, 0x40};
 

Спасибо вам за вашу помощь!

Ответ №1:

Вы можете использовать sscanf() для преобразования 2 байтов за раз из исходной строки в целевой массив:

 #include <stdint.h>
#include <stdio.h>

size_t convert_hex(uint8_t *dest, size_t count, const char *src) {
    size_t i;
    int value;
    for (i = 0; i < count amp;amp; sscanf(src   i * 2, "%2x", amp;value) == 1; i  ) {
        dest[i] = value;
    }
    return i;
}
 

Однако обратите внимание, что этот подход может быть неэффективным с квадратичной сложностью по времени для архитектур, в которых стандартная библиотека вычисляет длину исходной строки для каждого вызова sscanf() . Использование промежуточного массива решает эту проблему:

 #include <stdint.h>
#include <stdio.h>

size_t convert_hex(uint8_t *dest, size_t count, const char *src) {
    char buf[3];
    size_t i;
    int value;
    for (i = 0; i < count amp;amp; *src; i  ) {
        buf[0] = *src  ;
        buf[1] = '';
        if (*src) {
            buf[1] = *src  ;
            buf[2] = '';
        }
        if (sscanf(buf, "%x", amp;value) != 1)
            break;
        dest[i] = value;
    }
    return i;
}
 

Легко хранить результат преобразования непосредственно в dest массив:

 #include <stdint.h>
#include <stdio.h>

size_t convert_hex(uint8_t *dest, size_t count, const char *src) {
    char buf[3];
    size_t i;
    for (i = 0; i < count amp;amp; *src; i  ) {
        buf[0] = *src  ;
        buf[1] = '';
        if (*src) {
            buf[1] = *src  ;
            buf[2] = '';
        }
        if (sscanf(buf, "%hhx", amp;dest[i]) != 1)
            break;
    }
    return i;
}
 

Пуристы могут утверждать , что %hhx ожидает указатель на an unsigned char вместо a uint8_t , поэтому формат должен быть "%"SCNx8 или "%2"SCNx8 , определенным в <inttypes.h> , но эти альтернативы менее удобочитаемы и не нужны, поскольку тип uint8_t всегда идентичен типу unsigned char в архитектурах, где он определен.

Комментарии:

1. большое вам спасибо! знаете ли вы, как распечатать массив uint8_t? если я выполняю printf(«%d», myarray[0]), он выводит десятичное значение первого элемента myarray. как я могу напечатать точный элемент (например, 0xFF)?

2. @WonderWhy: десятичные и шестнадцатеричные числа-это просто представления точного значения… Используется printf("0xX", myarray[0]) для получения шестнадцатеричного представления с 0x префиксом. Удалите 0x , чтобы получить только 2 шестнадцатеричных байта.

Ответ №2:

Первое, что вам нужно усвоить, — это то, что десятичные, шестнадцатеричные или даже восьмеричные числа-это то, как представлены двоичные числа, хранящиеся в памяти компьютера. Как только вы сохраните байты в своем массиве, они больше не будут шестнадцатеричными.

Теперь о том, как решить вашу проблему: вам нужно извлечь два символа одновременно, затем преобразовать каждый символ в соответствующее целочисленное значение и использовать бит-рубашку и бит-или объединить их в однобайтовое значение.

Преобразование цифр 0 9 в их соответствующее значение легко, так как в спецификации C говорится, что они должны быть закодированы последовательно (т. Е. '0' должны поступать непосредственно перед '1' и так далее). Это означает, что вы можете использовать простое вычитание, '0' чтобы получить числовое значение ( '0' - '0' == 0 и '1' - '0' == 1 т. Д.).

Буквы A F , которые нужно прочитать, сложнее, потому что существует множество различных возможных кодировок, в том числе некоторые, которые не размещают буквы последовательно. С учетом сказанного, наиболее распространенной кодировкой ASCII является выполнение этого, что означает, что в большинстве систем вы можете использовать тот же «трюк», что и для десятичных цифр.

Затем, как их объединить, первое значение должно быть сдвинуто вверх (влево) на четыре бита, а затем растрировано ИЛИ дополнено вторым значением.

Результатом этого должен быть один байт, значение которого совпадает с двумя шестнадцатеричными цифрами из строки. Добавьте это значение в массив.

Комментарии:

1. Вероятно, мой вопрос был совсем не ясен, и я прошу прощения за это. Мне нужно только время от времени извлекать символы из строки и помещать их в массив uint8_t. Например, мой пустой массив-uint8_t myarray = {}, я извлекаю «02» из строки и добавляю их в myarray, это будет myarray ={ 0x02 }

2. Стандартная функция strtol на языке Си может быть полезна, но затем вы будете использовать временную строку с нулевым окончанием, в которую вы копируете два символа одновременно.

Ответ №3:

С

 char data[] = "02012B1530A6E3958A98530031902003876940000000000CDF9844173BE512AFFFFFFE11DBBA1F00079387800E13012E11FC017FFFFFFFFE39C10F40";
char *p = data;
 

первый байт равен

 (hexdigit(p[0]) << 4)   hexdigit(p[1])
 

Вы можете повторить приведенное выше выражение с помощью

 do {
    uint8_t value = (hexdigit(p[0]) << 4)   hexdigit(p[1]);
    p  = 2;
} while (*p);
 

для всех значений (убедитесь data , что в них четное количество символов).

Функция hexdigit() (реализация оставлена в качестве упражнения) преобразуется '0' в 0 , '1' в 1 , …, 'a' в 10 , 'A' в 10 ,…