如何分割 ASCII 字符的十六进制字节

3

我想要做的基本上是

例如:'a' 的十六进制等价物是 0x61,我能否将 61 分解成 61 并将它们存储为 '6''1'

一个缓冲区正在接收像这样的数据:

rx_dataframe.data[0] is H'00,'.'// H' is Hex equivalant and '' is ASCII value
rx_dataframe.data[0] is H'31,'1'
rx_dataframe.data[0] is H'32,'2'
rx_dataframe.data[0] is H'33,'3'

我需要将十六进制值0x00,0x31,0x32,0x33转换为字符值'0','0','3','1','3','2'; '3','3'并存储在tx_buff_data []的位置。

我希望我的tx_buff_data看起来像这样

tx_buff_data[0] have H'30,'0'
tx_buff_data[1] have H'30,'0'
tx_buff_data[2] have H'33,'3'
tx_buff_data[3] have H'31,'1'
tx_buff_data[4] have H'33,'3'
tx_buff_data[5] have H'32,'2'
tx_buff_data[6] have H'33,'3'
tx_buff_data[7] have H'33,'3'
7个回答

8

您可以使用位运算符AND + 移位将每个字节拆分为两个nibble(4位数量):

unsigned char lo = byte & 0x0f;
unsigned char hi = (byte >> 4) & 0x0f;

然后,您可以通过数组查找将每个半字转换为十六进制字符(因为字符串只是字符数组):

char loChar = "0123456789ABCDEF"[lo];
char hiChar = "0123456789ABCDEF"[hi];

我以前从没见过 "0123456789ABCDEF"[lo]; 这种写法。这是新的吗? - Fiddling Bits
1
@FiddlingBits 不是的,它与 const char *hex = "0123456789ABCDEF"; char loChar = hex[lo]; 是一样的,只是没有额外的本地变量。 - Drew McGowen
@FiddlingBits:“0123456789ABCDEF”是一个数组,[i]是用于访问偏移量为i(从零开始)的元素的括号符号。可能会让人惊讶的是,您可以使用相反顺序的参数和[]i["0123456789ABCDEF"]也有效,并且对于i==6,其计算结果为'6' - chqrlie

1
使用ASCII字符的定义可以导致极为省时和省代码的解决方案。 ASCII表显示:
values  0... 9 <=> figures '0'...'9' <=> 0x30...0x39 in ASCII code;
values 10...15 <=> figures 'A'...'F' <=> 0x41...0x46 in ASCII code;
                   or      'a'...'f' <=> 0x61...0x66 in ASCII code;

有时小写字母'a'到'f'会用于表示十六进制数的值。
因此,如果我们的半字节值低于10(0000b至1001b),则字符表示为0x30 + n或在c语法中为'0'+n。
如果n介于10和15之间(1010b至1111b),则表示为0x41 + n - 10或'A'+n-10。
使用无符号8位整型而不是char类型:
uint8_t nibble2char( uint8_t n ) {
  // make sure that n e {0x00,...,0x0F}
  if( n<10 ) return '0'+n;
  else       return 'A'+n-10;
}

或更短:
uint8_t nibble2char( uint8_t n ) {
  // make sure that n e {0x00,...,0x0F}
  return n<10 ? '0'+n : 'A'+n-10;
}

或者作为宏(感谢chqrlie提供(n)):
#define NIBBLE_TO_CHAR(n) ((n)<10 ? '0'+(n) : 'A'+(n)0)  // n <= 15 !

如果要使用小写字母,请将 'A' 替换为 'a'。
将带有 2 个半字节的字节转换为 2 字节的以 0 结尾的字符串,您可以使用以下方法:
void byte2str( uint8_t* s, uint8_t n ) {
  // s points to a string of at least 3 Bytes length !
  uint8_t hi = ( n >> 4 ) & 0x0F ;
  uint8_t lo =   n        & 0x0F ;
  s[0] = NIBBLE_TO_CHAR( hi );      // high byte
  s[1] = NIBBLE_TO_CHAR( lo );      // low byte
  s[2] = 0;
}

编辑:
通过chqrlie对宏的更正,该函数变得更加紧凑:

void byte2str( uint8_t* s, uint8_t n ) {
  // s points to a string of at least 3 Bytes length !
  s[0] = NIBBLE_TO_CHAR( ( n >> 4 ) & 0x0F );  // high byte
  s[1] = NIBBLE_TO_CHAR(   n        & 0x0F );  // low byte
  s[2] = 0;
}

1
#define NIBBLE_TO_CHAR(n) (n<10 ? '0'+n : 'A'+n-10) -> #define NIBBLE_TO_CHAR(n) ((n)<10 ? '0'+(n) : 'A'+(n)-10) - chqrlie

1
这是一个示例程序,可用作您代码的模板。
#include <stdio.h>

int main(void) 
{
    char in[] = { '\0', '1', '2', '3' };
    char out[2 * sizeof( in )];
    size_t  i;
    char *p;

    p = out;
    for ( i = 0; i < sizeof( in ) / sizeof( *in ); i++ )
    {
        *p = ( ( unsigned char )in[i] & 0xF0 ) >> 4;
        *p +=( *p < 10 ) ? '0' : 'A' - 10;
        ++p;
        *p = ( ( unsigned char )in[i] & 0x0F );
        *p +=( *p < 10 ) ? '0' : 'A' - 10;
        ++p;
    }

    for ( i = 0; i < sizeof( out ) / sizeof( *out ); i++ )
    {
        printf( "%c", out[i] );
    }

    puts( "" );

    return 0;
}

输出是

00313233

0

你可以使用 sprintf 将整数值转换为字符串,然后从字符串中提取单个字符。

#include <stdlib.h>

char *str;
sprintf(str, "%02x", 61)
char digitLow = str[0]; // '6'
char dightHigh = str[1]; // '1'

你们两个都是对的,我已经相应地更新了我的答案。 - Gio
1
你也许还想指出 str 被假定在其他地方被赋值,否则你的代码就会表现为 UB。 - Drew McGowen

0

进行转换的方法如下:

    uint8_t lo = byte & 0x0F
    uint8_t hi = (byte >> 4) & 0x0F

   char loChar = "0123456789ABCDEF"[lo];
   char hiChar = "0123456789ABCDEF"[hi];

如果 lohi 大于9,则此方法无效。 - Drew McGowen
1
此外,byte >> 8 是未定义的,而 % 0x0F 是模15,而不是16。 - Drew McGowen
我是指按位与运算符而不是取模运算符。我已经更正了答案。 - Gianluca Ghettini
我误解了这个问题。我以为每个nibble都在0-9的范围内。现在我发布了正确的答案。 - Gianluca Ghettini
1
你应该只有4,但是你还剩下了8。 - programmerjake
显示剩余3条评论

0
for(int i = 0, j = 0; i < data_input_size; i++, j += 2)
{
    int value = rx_dataframe.data[i];
    char str[10]; // really only need 3 but extra space in case of bugs
    sprintf(str, "%02X", value);
    tx_buff_data[j] = str[0];
    tx_buff_data[j + 1] = str[1];
}

int value 应该改为 unsigned。同时,这个假设是在嵌入式平台上可用 sprintf - Drew McGowen

0
我提议另一种方案,将固定大小的输入进行转换,并检查输出的大小:
void convert(char *output, int size_out, char *input, int size_in)
{
    static char hexdigit[] = "0123456789ABCDEF";
    while (size_in-- && size_out>2) {   // if not enough space, truncate 
        *output++ = hexdigit[(*input >> 4) & 0x0F];  // because char could be signed
        *output++ = hexdigit[*input & 0x0F];
        input++; 
        size_out -= 2;
    }
    *output++ = '\0';
}

由于它是为微控制器设计的,因此避免了任何类似sprintf()的开销。您可以这样调用它:

   convert(tx_buff_data, sizeof(tx_buff_data), your_input_buffer, number_of_bytes); 

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