如何在C中将字节数组转换为十六进制字符串?

Ste*_*lsh 77 c string hex

我有:

uint8 buf[] = {0, 1, 10, 11};
Run Code Online (Sandbox Code Playgroud)

我想将字节数组转换为字符串,以便我可以使用printf打印字符串:

printf("%s\n", str);
Run Code Online (Sandbox Code Playgroud)

得到(冒号没有必要):

"00:01:0A:0B"
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激.

Mar*_*iec 82

printf("%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3]);
Run Code Online (Sandbox Code Playgroud)

对于更通用的方式:

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

要连接到字符串,有几种方法可以做到这一点...我可能会保持指向字符串末尾的指针并使用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");
Run Code Online (Sandbox Code Playgroud)

  • `printf("%02X",(unsigned char)buf [i]);`应该使用`原文会导致无符号字符溢出 (5认同)
  • 为什么不`printf("%02hhX",buf [i])`? (3认同)

kri*_*iss 26

对于completude,您也可以轻松地执行它而无需调用任何繁重的库函数(没有snprintf,没有strcat,甚至没有memcpy).它可能很有用,比如你是在编写一些没有libc的微控制器或OS内核.

如果你谷歌的话,你可以找到类似的代码.真的,它比调用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);
}
Run Code Online (Sandbox Code Playgroud)

这是另一个略短的版本.它只是避免中间索引变量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);
}
Run Code Online (Sandbox Code Playgroud)

下面是另一个回答评论的版本,说我使用"技巧"来了解输入缓冲区的大小.实际上,这不是技巧,而是必要的输入知识(您需要知道要转换的数据的大小).通过将转换代码提取到单独的函数,我更清楚了.我还为目标缓冲区添加了边界检查代码,如果我们知道自己在做什么,这并不是必需的.

#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);
}
Run Code Online (Sandbox Code Playgroud)


Yan*_*uth 15

这是一种更快的方法:

#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
}
Run Code Online (Sandbox Code Playgroud)

  • 此代码包含一个错误,该错误仅在奇怪的非可打印输入上显示(没有时间深入研究数学上正在发生的事情).尝试十六进制编码'ca9e3c972f1c5db40c0b4a66ab5bc1a20ca4457bdbe5e0f8925896d5ed37d726`的二进制,你会得到`ÌaÌe3cÌ72f1c5dÌ40c0b4a66Ìb5bÌ1Ì20cÌ4457bÌbÌ5Ì0Ì8Ì258Ì6Ì5Ìd37Ì726`出来.要解决此问题,for循环第一行中`hex_str`内的位需要更改为`(input [i] >> 4)&0x0F`,如@ kriss的答案.然后它工作正常. (3认同)

raz*_*zak 8

上面已经存在类似的答案,我添加了这个来解释下面的代码行是如何工作的:

ptr += sprintf (ptr, "%02X", buf[i])
Run Code Online (Sandbox Code Playgroud)

这很安静,不易理解,我将解释放在下面的评论中:

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);
Run Code Online (Sandbox Code Playgroud)


7vu*_*0hy 7

解决方案

函数btox将任意数据*bb转换为未终止*xpn十六进制数字字符串:

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];
}
Run Code Online (Sandbox Code Playgroud)

例子

#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);
}
Run Code Online (Sandbox Code Playgroud)

结果:00010A0B

直播:Tio.run


sda*_*aau 5

我只是想添加以下内容,即使它稍微偏离主题(不是标准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(). 
Run Code Online (Sandbox Code Playgroud)

...但是,标准的用户空间似乎不存在这些格式说明符(s)printf.