【问题标题】:How to split hex byte of an ASCII character如何拆分 ASCII 字符的十六进制字节
【发布时间】:2014-10-09 05:39:44
【问题描述】:

基本上我想做的是

例如:'a' hex 等价物是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'

我需要将hex values 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'

【问题讨论】:

    标签: c microcontroller low-level-code


    【解决方案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'。

    因此,如果我们的 nibble 的值低于 10(0000b 到 1001b),则字符表示将是 0x30 + n,或者在 c 语法中为 '0'+n。
    如果 n 在 10 和 15 之间(1010b 到 1111b)0x41 + n - 10 或 'A'+n-10。
    使用 unsigned int 8bit 代替 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;
    }
    

    【讨论】:

    • #define NIBBLE_TO_CHAR(n) (n&lt;10 ? '0'+n : 'A'+n-10) -> #define NIBBLE_TO_CHAR(n) ((n)&lt;10 ? '0'+(n) : 'A'+(n)-10)
    【解决方案2】:

    要转换你可以这样做:

        uint8_t lo = byte & 0x0F
        uint8_t hi = (byte >> 4) & 0x0F
    
       char loChar = "0123456789ABCDEF"[lo];
       char hiChar = "0123456789ABCDEF"[hi];
    

    【讨论】:

    • 如果lohi 大于9,这将不起作用
    • 显然你的输入应该和你想做的一致:-)
    • 另外,byte &gt;&gt; 8 是未定义的,% 0x0F 是模 15,而不是 16。
    • @G_G: 'J' (ASCII 0x4A) 输入不一致吗?
    • 你还有 >> 8 而你应该有 >> 4
    【解决方案3】:

    这是一个示例程序,可用作您的代码模板

    #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
    

    【讨论】:

      【解决方案4】:

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

      #include <stdlib.h>
      
      char *str;
      sprintf(str, "%02x", 61)
      char digitLow = str[0]; // '6'
      char dightHigh = str[1]; // '1'
      

      【讨论】:

      • 你说得对,我已经相应地更新了我的答案。
      • 您可能还想指出 str 被假定分配给其他地方,否则您的代码会按原样显示 UB。
      【解决方案5】:

      我建议你另一种变体,对固定大小的输入进行转换,并检查输出的大小:

      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); 
      

      【讨论】:

        【解决方案6】:
        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 在嵌入式平台上可用。
        【解决方案7】:

        您可以使用按位 AND + 移位将每个字节分成两个半字节(4 位数量):

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

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

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

        【讨论】:

        • 我以前从未见过"0123456789ABCDEF"[lo];。是新的吗?
        • @FiddlingBits 不,它和const char *hex = "0123456789ABCDEF"; char loChar = hex[lo]; 一样,只是没有额外的局部变量。
        • @FiddlingBits: "0123456789ABCDEF" 是一个数组,[i] 是括号符号,用于访问偏移量 i 处的元素(从零开始)。令人惊讶的是,您可以将[] 与相反顺序的参数一起使用:i["0123456789ABCDEF"] 也可以工作并计算为'6' for i==6
        猜你喜欢
        • 1970-01-01
        • 2020-06-11
        • 2015-07-19
        • 2013-05-29
        • 1970-01-01
        • 2014-01-02
        • 1970-01-01
        • 2017-08-23
        • 2019-07-27
        相关资源
        最近更新 更多