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

117

我有:

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

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

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

获取(冒号不必要):

"00:01:0A:0B"
任何帮助都将不胜感激。

buf[i] must be casted to unsigned char, or it will overflow if buf[i] > 127, that is:buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]); - whatacold
19个回答

1

你可以使用snprintf和malloc解决问题。

char c_buff[50];

u8_number_val[] = { 0xbb, 0xcc, 0xdd, 0x0f, 0xef, 0x0f, 0x0e, 0x0d, 0x0c };

char *s_temp = malloc(u8_size * 2 + 1);

for (uint8_t i = 0; i < u8_size; i++)
{
    snprintf(s_temp  + i * 2, 3, "%02x", u8_number_val[i]);
}

snprintf(c_buff, strlen(s_temp)+1, "%s", s_temp );

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

free(s);

输出:

bbccdd0fef0f0e0d0c


0

我知道这个问题已经有答案了,但我认为我的解决方案可能会对某些人有所帮助。

在我的情况下,我有一个表示密钥的字节数组,并且我需要将这个字节数组转换为十六进制值的字符数组,以便在一行中打印出来。我将我的代码提取到一个函数中,像这样:

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]


0

ZincX的解决方案已经适配包含冒号分隔符:

char buf[] = {0,1,10,11};
int i, size = sizeof(buf) / sizeof(char);
char *buf_str = (char*) malloc(3 * size), *buf_ptr = buf_str;
if (buf_str) {
  for (i = 0; i < size; i++)
    buf_ptr += sprintf(buf_ptr, i < size - 1 ? "%02X:" : "%02X\0", buf[i]);
  printf("%s\n", buf_str);
  free(buf_str);
}

0

我会在这里添加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.
}

它将打印长度为countbuffer的十六进制转储到std::ostream out(您可以将其默认设置为std::cout)。每行将包含bytes_per_line字节,每个字节都用大写两位十六进制表示。字节之间会有一个空格。在行尾或缓冲区末尾,它将打印一个换行符。如果将bytes_per_line设置为0,则不会打印新行。请自行尝试。


0

为了简单使用,我编写了一个函数来对输入的字符串(二进制数据)进行编码:

/* Encodes string to hexadecimal string reprsentation
    Allocates a new memory for supplied lpszOut that needs to be deleted after use
    Fills the supplied lpszOut with hexadecimal representation of the input
    */
void StringToHex(unsigned char *szInput, size_t size_szInput, char **lpszOut)
{
    unsigned char *pin = szInput;
    const char *hex = "0123456789ABCDEF";
    size_t outSize = size_szInput * 2 + 2;
    *lpszOut = new char[outSize];
    char *pout = *lpszOut;
    for (; pin < szInput + size_szInput; pout += 2, pin++)
    {
        pout[0] = hex[(*pin >> 4) & 0xF];
        pout[1] = hex[*pin & 0xF];
    }
    pout[0] = 0;
}

使用方法:

unsigned char input[] = "This is a very long string that I want to encode";
char *szHexEncoded = NULL;
StringToHex(input, strlen((const char *)input), &szHexEncoded);

printf(szHexEncoded);

// The allocated memory needs to be deleted after usage
delete[] szHexEncoded;

0
在C语言中没有这个原始类型。我可能会使用malloc(或者alloca)分配一个足够长的缓冲区,并循环处理输入。我也看到过使用动态字符串库来实现,其语义(但不是语法!)类似于C++的ostringstream,这是一种更通用的解决方案,但仅针对单个情况可能并不值得增加额外的复杂性。

0
以下可能会有帮助。
/**
 * @brief Convert an arbitrary length byte array to a hex representation
 * 
 * @param bytes 
 * @param array_length 
 * @param output 
 */
void bytes_to_hex(uint8_t *bytes, uint8_t array_length, char *output)
{

    for (int i = 0, j = 0; i < array_length; i++, j+=2)
    {
        uint8_t bottom = bytes[i] % 16;
        uint8_t top = bytes[i] / 16;
        output[j] = (top > 9 ? 'A' - 10 : '0') + top;
        output[j+1] = (bottom > 9 ? 'A' - 10 : '0') + bottom;
    }
}

-1

如果您想将十六进制值存储在char *字符串中,可以使用snprintf。您需要为所有打印的字符分配空间,包括前导零和冒号。

扩展Mark的答案:

char str_buf* = malloc(3*X + 1);   // X is the number of bytes to be converted

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) snprintf(str_buf, 1, ":");
    snprintf(str_buf, 2, "%02X", num_buf[i]);  // need 2 characters for a single hex value
}
snprintf(str_buf, 2, "\n\0"); // dont forget the NULL byte

现在str_buf将包含十六进制字符串。


1
这会一遍又一遍地覆盖前两个字符...对吗? - xordon

-2

这些是多么复杂的解决方案啊!
Malloc、sprints和casts,哦,我的天。(来自OZ的引用)
丝毫没有任何遗漏。哇

那么像这样怎么样?

main()
{
    // the value
    int value = 16;

    // create a string array with a '\0' ending ie. 0,0,0
    char hex[]= {0,0,'\0'}; 
    char *hex_p=hex;

    //a working variable
    int TEMP_int=0;

    // get me how many 16s are in this code
    TEMP_int=value/16;

    // load the first character up with 
    // 48+0 gives you ascii 0, 55+10 gives you ascii A
    if (TEMP_int<10) {*hex_p=48+TEMP_int;}
        else {*hex_p=55+TEMP_int;}

    // move that pointer to the next (less significant byte)<BR>
    hex_p++;

    // get me the remainder after I have divied by 16
    TEMP_int=value%16;

    // 48+0 gives you ascii 0, 55+10 gives you ascii A
    if (TEMP_int<10) {*hex_p=48+TEMP_int;}
        else {*hex_p=55+TEMP_int;}

    // print the result
    printf("%i , 0x%s",value,hex);

}

好的,现在你有两个十六进制数字。接下来需要添加分隔符并处理其他字节进行转换。也许可以使用循环?将其制作成一个函数,你就会得到类似于我的代码(但可能比较冗长且难以阅读)。在批评其他帖子之前,或许你应该先完成这项工作? - kriss
1
关于源代码中的注释(不是REM,那是用于注释的BASIC关键字,请避免使用):用英语写出代码正在做什么的注释是非常糟糕的实践!是的,程序员应该知道模运算符的含义(给出余数),以及除法计算一个数字在另一个数字中出现的次数...还有printf打印结果。哦天啊! - kriss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接