【问题标题】:Convert two ASCII Hexadecimal Characters (Two ASCII bytes) in one byte将两个 ASCII 十六进制字符(两个 ASCII 字节)转换为一个字节
【发布时间】:2011-10-19 11:15:07
【问题描述】:

我想将两个 ASCII 字节转换为一个十六进制字节。 例如。

0x30 0x43 => 0x0C , 0x34 0x46 => 0x4F ...

ASCII 字节是介于 09 之间的数字或介于 AF 之间的字母(仅限大写字母),因此介于 0x30 ... 0x390x41 之间。 ..0x46

我知道如何用数字0x340x46 : 0x4F = 0x34 * 0x10 + 0x46“构造”0x4F

所以,事实上,我会将一个 ASCII 字节转换为十六进制值。

为此,我可以构建和排列以将十六进制值分配给 ASCII 字符:

0x30 => 0x00
0x31 => 0x01
...
0x46 => 0x0F

但是,也许它有一个最“合适”的解决方案。

该程序将在 AVR µC 上运行并使用 avr-gcc 编译,因此 scanf() / printf() 解决方案不适合。

你有想法吗? 谢谢

【问题讨论】:

  • 没有 ASCII 字节或十六进制字节之类的东西。
  • 您(显然!)想要做的是 convert 一对字节到单个字节,通过将两个字节解释为 ASCII 符号它又代表十六进制数字,并发出相应的十六进制值。解决这类问题需要准确了解你真正在做什么;和精确的沟通也有很大帮助:) 你必须确保你从根本上理解什么是数据。正如大卫所说,字节只是字节;没有 ASCII 字节或十六进制字节。
  • ASCII 字节 = 表示 ASCII 字符的字节,十六进制字节 = 以十六进制表示的字节。我知道字节只是字节;我只是不知道该怎么解释。但其他人已经明白我的意思了。不是最重要的吗?

标签: c ascii hex avr-gcc


【解决方案1】:

将 2 个十六进制字符转换为一个字节分两步完成:

  1. char ab 转换为它们的数字(例如 'F' -> 0xF),这是在两个大的 if else 分支中完成的,检查字符是否在范围'0''9''A''F''a''f'

  2. 在第二步中,通过将a(最大值为0xF0b0000_FFFF))4 向左移动(a << 4 -> 0b1111_0000)来连接两个数字和然后对ab ((a << 4) | b) 应用按位或运算:

a: 0000_1111
b: 1111_0000
-> 1111_1111
#include <stdio.h>
#include <stdint.h>

#define u8 uint8_t
#define u32 uint32_t

u8 to_hex_digit(char a, char b) {
    u8 result = 0;

    if (a >= 0x30 && a <= 0x39) {
        result = (a - 0x30) << 4;
    } else if (a >= 0x41 && a <= 0x46) {
        result = (a - 0x41 + 10) << 4;
    } else if (a >= 0x61 && a <= 0x7A) {
        result = (a - 0x61 + 10) << 4;
    } else {
        printf("invalid hex digit: '%c'\n", a);
    }

    if (b >= 0x30 && b <= 0x39) {
        result |= b - 0x30;
    } else if (b >= 0x41 && b <= 0x46) {
        result |= b - 0x41 + 10;
    } else if (b >= 0x61 && b <= 0x7A) {
        result |= b - 0x61 + 10;
    } else {
        printf("invalid hex digit: '%c'\n", b);
    }

    return result;
}

u32 main() {
    u8 result = to_hex_digit('F', 'F');
    printf("0x%X (%d)\n", result, result);

    return 0;
}

【讨论】:

    【解决方案2】:

    这是一个同时适用于大写和小写十六进制字符串的版本:

    void hex_decode(const char *in, size_t len, uint8_t *out)
    {
      unsigned int i, hn, ln;
      char hc, lc;
    
      memset(out, 0, len);
    
      for (i = 0; i < 2*len; i += 2) {
    
        hc = in[i];
        if ('a' <= hc && hc <= 'f') hc = toupper(hc);
        lc = in[i+1];
        if ('a' <= lc && lc <= 'f') lc = toupper(lc);
    
        hn = hc > '9' ? hc - 'A' + 10 : hc - '0';
        ln = lc > '9' ? lc - 'A' + 10 : lc - '0';
    
        out[i >> 1] = (hn << 4 ) | ln;
      }
    }
    

    【讨论】:

      【解决方案3】:

      它的作品,但可以大大优化!

      inline uint8_t  twoAsciiByteToByte(const std::string& s)
      {
          uint8_t r = 0;
      
          if (s.length() == 4)
          {
              uint8_t a = asciiToByte(s[0]);
              uint8_t b = asciiToByte(s[1]);
              uint8_t c = asciiToByte(s[2]);
              uint8_t d = asciiToByte(s[3]);
      
              int h = (a * 10 + b);
              int l = (c * 10 + d);
      
              if (s[0] == '3')
                  h -= 30;
              else if (s[0] == '4')
                  h -= 31;
      
              if (s[2] == '3')
                  l -= 30;
              else if (s[2] == '4')
                  l -= 31;
      
              r = (h << 4) | l;
          }
      
          return r;
      }
      

      【讨论】:

      • inline uint8_t asciiToByte(char c) { if (c >= '0' && c = 'A' && c
      【解决方案4】:

      任务:

      将包含十六进制 ascii 字符的字符串转换为其字节值 所以 ascii "FF" 变成 0xFF 和 ascii "10" (x31x30x00) 变成 0x10

      char asciiString[]="aaAA12fF";// input ascii hex string 
      char result[4];               // byte equivalent of the asciiString (the size should be at half of asciiString[])
      

      //最终的结果应该是:

      result[0] = 0xAA;
      result[1] = 0xAA;       
      result[2] = 0x12;
      result[3] = 0xFF;
      

      //1.第一步:转换 asciiString 使其仅包含大写:

      // convert string to upper cases:
      stringToUpperCases(asciiString);
      

      使用:

      void stringToUpperCases(char *p)
      {   
          for(int i=0; *(p+i) !='\0'; i++)
          {
              *(p+i) = (unsigned char) toupper( *(p+i) );
          }
      }
      

      //2.将包含十六进制 ascii 字符的字符串转换为其字节值:

      // convert string to bytes:
      
      int nrOfBytes = stringToBytes(asciiString,result);
      
      //use:  
      unsigned char charToHexDigit(char c)
      {
      if (c >= 'A')
          return (c - 'A' + 10);
      else
          return (c - '0');
      }
      
      unsigned char ascii2HexToByte(char *ptr)
      {
          return charToHexDigit( *ptr )*16 + charToHexDigit( *(ptr+1) );
      }
      
      int stringToBytes(char *string, char *result)
      {
          int k=0;
          int strLen = strlen(string);
      
          for(int i = 0; i < strLen; i = i + 2)
          {
              result[k] = ascii2HexToByte( &string[i] );
              k++;
          }
      
          return k; // number of bytes in the result array 
      }   
      

      //3.打印结果:

      printNrOfBytes(nrOfBytes, result);
      

      // 使用:

      void printNrOfBytes(int nr, char *p)
      {
         for(int i= 0; i < nr; i++)
          {
              printf( "0x%02X ", (unsigned char)*(p+i) );
          }
          printf( "\n");
      }
      

      //4.结果应该是:

      0xAA 0xAA 0x12 0xFF

      //5.这是测试程序:

      char asciiString[]="aaAA12fF"; // input ascii hex string 
      char result[4];                // result  
      // convert string to upper cases:
      stringToUpperCases(asciiString);
      
      // convert string to bytes
      int nrOfBytes = stringToBytes(asciiString,result);
      
      // print result:
      printNrOfBytes(nrOfBytes, result);
      
      // result:
      //  0xAA 0xAA 0x12 0xFF
      

      【讨论】:

        【解决方案5】:

        我无法理解您的示例,但是如果您想将包含十六进制 ascii 字符的字符串转换为其字节值(例如,字符串“56”变为字节 0x56,您可以使用它(假设您的系统正在使用 ASCII)

        uint8_t*
        hex_decode(const char *in, size_t len,uint8_t *out)
        {
                unsigned int i, t, hn, ln;
        
                for (t = 0,i = 0; i < len; i+=2,++t) {
        
                        hn = in[i] > '9' ? in[i] - 'A' + 10 : in[i] - '0';
                        ln = in[i+1] > '9' ? in[i+1] - 'A' + 10 : in[i+1] - '0';
        
                        out[t] = (hn << 4 ) | ln;
                }
        
                return out;
        }
        

        你会像使用它一样使用它。

        char x[]="1234";
        uint8_t res[2];
        hex_decode(x,strlen(x),res);
        

        而 res(必须至少是 in 参数长度的一半)现在包含 2 个字节 0x12,0x34

        另请注意,此代码需要十六进制字母 A-F 大写,a-f 不会(而且它不做任何错误检查 - 所以你必须传递有效的东西)。

        【讨论】:

        • 使用(in[i]|32)-'a'+10,现在您可以支持大写和小写(很可能只需要在机器代码中添加一条指令)。
        • uint8_t 不是一个字节吗?如果它应该包含两个字节,那不应该是 uint16_t 吗?
        • @Rudy Velthuis 它应该是一个字节,因为它包含 1 个字节,而不是 2 个
        • @nos:是的,我看到它是一个 uint8_t 数组。放屁,对不起。
        • 确保包含 cytpe.h
        【解决方案6】:

        您可以使用strtol(),它是avr-libc 的一部分,或者您可以很容易地只写您的具体案例:

        unsigned char charToHexDigit(char c)
        {
          if (c >= 'A')
            return c - 'A' + 10;
          else
            return c - '0';
        }
        
        unsigned char stringToByte(char c[2])
        {
          return charToHexDigit(c[0]) * 16 + charToHexDigit(c[1]);
        }
        

        【讨论】:

        • 好的,这很容易......我认为 0 到 9 之间的数字左旋转 4 位,但解决方案更简单!谢谢
        • 开个玩笑? : $ man ascii Aucune entrée de manuel pour ascii
        • @Loic:不,你一定是错过了手册页。这是英文版:linux.die.net/man/7/ascii
        猜你喜欢
        • 1970-01-01
        • 2012-10-29
        • 2012-08-08
        • 1970-01-01
        • 2011-11-21
        • 1970-01-01
        • 2015-03-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多