Как преобразовать байтовый массив в шестнадцатеричную строку в C? - PullRequest
74 голосов
/ 15 июня 2011

У меня есть:

uint8 buf[] = {0, 1, 10, 11};

Я хочу преобразовать массив байтов в строку так, чтобы я мог напечатать строку, используя printf:

printf("%s\n", str);

и получить (двоеточиене нужно):

"00:01:0A:0B"

Любая помощь будет принята с благодарностью.

Ответы [ 17 ]

76 голосов
/ 15 июня 2011
printf("%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3]);

для более общего способа:

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) printf(":");
    printf("%02X", buf[i]);
}
printf("\n");

чтобы объединить строку, есть несколько способов сделать это ... я бы, вероятно, сохранил указатель на конец строки и использовал бы sprintf. Вы также должны отслеживать размер массива, чтобы убедиться, что он не становится больше, чем выделенное пространство:

int i;
char* buf2 = stringbuf;
char* endofbuf = stringbuf + sizeof(stringbuf);
for (i = 0; i < x; i++)
{
    /* i use 5 here since we are going to add at most 
       3 chars, need a space for the end '\n' and need
       a null terminator */
    if (buf2 + 5 < endofbuf)
    {
        if (i > 0)
        {
            buf2 += sprintf(buf2, ":");
        }
        buf2 += sprintf(buf2, "%02X", buf[i]);
    }
}
buf2 += sprintf(buf2, "\n");
25 голосов
/ 11 октября 2012

Для полного завершения, вы также можете легко сделать это без вызова какой-либо тяжелой библиотечной функции (без snprintf, без strcat, даже с memcpy).Это может быть полезно, например, если вы программируете какой-нибудь микроконтроллер или ядро ​​ОС, где libc недоступен.

Ничего особенного, вы не можете найти похожий код, если гуглите его.На самом деле это не намного сложнее, чем вызов snprintf, и намного быстрее.

#include <stdio.h>

int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    int i = 0;
    for(; i < sizeof(buf)-1; ++i){
        *pout++ = hex[(*pin>>4)&0xF];
        *pout++ = hex[(*pin++)&0xF];
        *pout++ = ':';
    }
    *pout++ = hex[(*pin>>4)&0xF];
    *pout++ = hex[(*pin)&0xF];
    *pout = 0;

    printf("%s\n", str);
}

Вот еще одна более короткая версия.Он просто избегает промежуточной индексной переменной i и дублирует последний регистр кода (но завершающий символ пишется два раза).

#include <stdio.h>
int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    for(; pin < buf+sizeof(buf); pout+=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
    }
    pout[-1] = 0;

    printf("%s\n", str);
}

Ниже приведена еще одна версия, чтобы ответить на комментарий о том, что я использовал "трюк"знать размер входного буфера.На самом деле это не уловка, а необходимые входные знания (вам нужно знать размер данных, которые вы конвертируете).Я сделал это более понятным, выделив код преобразования в отдельную функцию.Я также добавил код проверки границы для целевого буфера, который не является действительно необходимым, если мы знаем, что делаем.

#include <stdio.h>

void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
    unsigned char * pin = in;
    const char * hex = "0123456789ABCDEF";
    char * pout = out;
    for(; pin < in+insz; pout +=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
        if (pout + 3 - out > outsz){
            /* Better to truncate output string than overflow buffer */
            /* it would be still better to either return a status */
            /* or ensure the target buffer is large enough and it never happen */
            break;
        }
    }
    pout[-1] = 0;
}

int main(){
    enum {insz = 4, outsz = 3*insz};
    unsigned char buf[] = {0, 1, 10, 11};
    char str[outsz];
    tohex(buf, insz, str, outsz);
    printf("%s\n", str);
}
15 голосов
/ 17 июня 2013

Вот метод, который намного быстрее:

#include <stdlib.h>
#include <stdio.h>

unsigned char *     bin_to_strhex(const unsigned char *bin, unsigned int binsz,
                                  unsigned char **result)
{
  unsigned char     hex_str[]= "0123456789abcdef";
  unsigned int      i;

  if (!(*result = (unsigned char *)malloc(binsz * 2 + 1)))
    return (NULL);

  (*result)[binsz * 2] = 0;

  if (!binsz)
    return (NULL);

  for (i = 0; i < binsz; i++)
    {
      (*result)[i * 2 + 0] = hex_str[(bin[i] >> 4) & 0x0F];
      (*result)[i * 2 + 1] = hex_str[(bin[i]     ) & 0x0F];
    }
  return (*result);
}

int                 main()
{
  //the calling
  unsigned char     buf[] = {0,1,10,11};
  unsigned char *   result;

  printf("result : %s\n", bin_to_strhex((unsigned char *)buf, sizeof(buf), &result));
  free(result);

  return 0
}
7 голосов
/ 15 декабря 2016

Подобные ответы уже существуют выше, я добавил этот, чтобы объяснить, как точно работает следующая строка кода:

ptr += sprintf (ptr, "%02X", buf[i])

Это довольно сложно и непросто для понимания, я поставил объяснение в комментариях ниже:

uint8 buf[] = {0, 1, 10, 11};

/* Allocate twice the number of the bytes in the buf array because each byte would be 
 * converted to two hex characters, also add an extra space for the terminating null byte
 * [size] is the size of the buf array */
char output[(size * 2) + 1];

/* pointer to the first item (0 index) of the output array */
char *ptr = &output[0];

int i;

for (i = 0; i < size; i++)
{
    /* sprintf converts each byte to 2 chars hex string and a null byte, for example
     * 10 => "0A\0".
     *
     * These three chars would be added to the output array starting from
     * the ptr location, for example if ptr is pointing at 0 index then the hex chars
     * "0A\0" would be written as output[0] = '0', output[1] = 'A' and output[2] = '\0'.
     *
     * sprintf returns the number of chars written execluding the null byte, in our case
     * this would be 2. Then we move the ptr location two steps ahead so that the next
     * hex char would be written just after this one and overriding this one's null byte.
     *
     * We don't need to add a terminating null byte because it's already added from 
     * the last hex string. */  
    ptr += sprintf (ptr, "%02X", buf[i]);
}

printf ("%s\n", output);
5 голосов
/ 30 октября 2013

Я просто хотел добавить следующее, даже если это немного не по теме (не в стандартном C), но я часто ищу его и сталкиваюсь с этим вопросом среди первых поисковых запросов. Функция печати ядра Linux printk также имеет спецификаторы формата для вывода содержимого массива / памяти «напрямую» через спецификатор единственного формата:

https://www.kernel.org/doc/Documentation/printk-formats.txt

Raw buffer as a hex string:
    %*ph    00 01 02  ...  3f
    %*phC   00:01:02: ... :3f
    %*phD   00-01-02- ... -3f
    %*phN   000102 ... 3f

    For printing a small buffers (up to 64 bytes long) as a hex string with
    certain separator. For the larger buffers consider to use
    print_hex_dump(). 

... однако, эти спецификаторы формата, кажется, не существуют для стандартного пользовательского пространства (s)printf.

2 голосов
/ 29 декабря 2018

Решение

Функция btox преобразует произвольные данные *bb в неопределенную строку *xp из n шестнадцатеричных цифр:

void btox(char *xp, const char *bb, int n) 
{
    const char xx[]= "0123456789ABCDEF";
    while (--n >= 0) xp[n] = xx[(bb[n>>1] >> ((1 - (n&1)) << 2)) & 0xF];
}

Пример

#include <stdio.h>

typedef unsigned char uint8;

void main(void) 
{
    uint8 buf[] = {0, 1, 10, 11};
    int n = sizeof buf << 1;
    char hexstr[n + 1];

    btox(hexstr, buf, n);
    hexstr[n] = 0; /* Terminate! */
    printf("%s\n", hexstr);
}

Результат: 00010A0B.

Live: Tio.run .

1 голос
/ 21 декабря 2016

Эта функция подходит, когда пользователь / вызывающая сторона хочет, чтобы шестнадцатеричная строка была помещена в массив / буфер символов.С шестнадцатеричной строкой в ​​символьном буфере пользователь / вызывающая сторона может использовать свой собственный макрос / функцию, чтобы отобразить или записать его в любое место (например, в файл).Эта функция также позволяет вызывающему абоненту контролировать количество (шестнадцатеричных) байтов для вставки в каждую строку.

/**
 * @fn 
 * get_hex
 *
 * @brief 
 * Converts a char into bunary string 
 *
 * @param[in]   
 *     buf Value to be converted to hex string
 * @param[in]   
 *     buf_len Length of the buffer
 * @param[in]   
 *     hex_ Pointer to space to put Hex string into
 * @param[in]   
 *     hex_len Length of the hex string space
 * @param[in]   
 *     num_col Number of columns in display hex string
 * @param[out]   
 *     hex_ Contains the hex string
 * @return  void
 */
static inline void
get_hex(char *buf, int buf_len, char* hex_, int hex_len, int num_col)
{
    int i;
#define ONE_BYTE_HEX_STRING_SIZE   3
  unsigned int byte_no = 0;

  if (buf_len <= 0) {
      if (hex_len > 0) {
        hex_[0] = '\0';
      }
      return;
  }

  if(hex_len < ONE_BYTE_HEX_STRING_SIZE + 1)
  {
      return;
  }

  do {
         for (i = 0; ((i < num_col) && (buf_len > 0) && (hex_len > 0)); ++i )
         {
            snprintf(hex_, hex_len, "%02X ", buf[byte_no++] & 0xff);
            hex_ += ONE_BYTE_HEX_STRING_SIZE;
            hex_len -=ONE_BYTE_HEX_STRING_SIZE;
            buf_len--;
         }
         if (buf_len > 1)
         {
             snprintf(hex_, hex_len, "\n");
             hex_ += 1;
         }
  } while ((buf_len) > 0 && (hex_len > 0));

}

Пример: код

#define DATA_HEX_STR_LEN 5000
    char      data_hex_str[DATA_HEX_STR_LEN];

    get_hex(pkt, pkt_len, data_hex_str, DATA_HEX_STR_LEN, 16);
    //      ^^^^^^^^^^^^                                  ^^
    //      Input byte array                              Number of (hex) byte
    //      to be converted to hex string                 columns in hex string

    printf("pkt:\n%s",data_hex_str) 

ВЫХОД

pkt:
BB 31 32 00 00 00 00 00 FF FF FF FF FF FF DE E5 
A8 E2 8E C1 08 06 00 01 08 00 06 04 00 01 DE E5 
A8 E2 8E C1 67 1E 5A 02 00 00 00 00 00 00 67 1E 
5A 01 
1 голос
/ 26 июля 2012

Это один из способов преобразования:

#include<stdio.h>
#include<stdlib.h>

#define l_word 15
#define u_word 240

char *hex_str[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};

main(int argc,char *argv[]) {


     char *str = malloc(50);
     char *tmp;
     char *tmp2;

     int i=0;


     while( i < (argc-1)) {
          tmp = hex_str[*(argv[i]) & l_word];
          tmp2 = hex_str[*(argv[i]) & u_word];

          if(i == 0) { memcpy(str,tmp2,1); strcat(str,tmp);}
          else { strcat(str,tmp2); strcat(str,tmp);}
          i++;
    }

    printf("\n*********  %s  *************** \n", str);

}
0 голосов
/ 01 декабря 2013

Я добавлю сюда версию C ++ для всех, кому интересно.

#include <iostream>
#include <iomanip>
inline void print_bytes(char const * buffer, std::size_t count, std::size_t bytes_per_line, std::ostream & out) {
    std::ios::fmtflags flags(out.flags()); // Save flags before manipulation.
    out << std::hex << std::setfill('0');
    out.setf(std::ios::uppercase);
    for (std::size_t i = 0; i != count; ++i) {
        auto current_byte_number = static_cast<unsigned int>(static_cast<unsigned char>(buffer[i]));
        out << std::setw(2) << current_byte_number;
        bool is_end_of_line = (bytes_per_line != 0) && ((i + 1 == count) || ((i + 1) % bytes_per_line == 0));
        out << (is_end_of_line ? '\n' : ' ');
    }
    out.flush();
    out.flags(flags); // Restore original flags.
}

Будет напечатан шестнадцатеричный код buffer длины count до std::ostream out (по умолчанию вы можете установить значение std::cout). Каждая строка будет содержать bytes_per_line байтов, каждый байт представлен в шестнадцатеричном формате с заглавными буквами. Между байтами будет пробел. И в конце строки или в конце буфера он напечатает новую строку. Если bytes_per_line установлен в 0, он не будет печатать new_line. Попробуйте сами.

0 голосов
/ 28 октября 2018

Я знаю, что на этот вопрос уже есть ответ, но я думаю, что мое решение могло бы кому-то помочь.

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

char const * keyToStr(uint8_t const *key)
{
    uint8_t offset = 0;
    static char keyStr[2 * KEY_SIZE + 1];

    for (size_t i = 0; i < KEY_SIZE; i++)
    {
        offset += sprintf(keyStr + offset, "%02X", key[i]);
    }
    sprintf(keyStr + offset, "%c", '\0');

    return keyStr;
}

Теперь я могу использовать свою функцию следующим образом:

Serial.print("Public key: ");
Serial.println(keyToStr(m_publicKey));

Serial объект является частью библиотеки Arduino, а m_publicKey является членом моего класса со следующим объявлением uint8_t m_publicKey[32].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...