【问题标题】:Hex to char array in CC中的十六进制到字符数组
【发布时间】:2010-12-06 03:45:48
【问题描述】:

给定一串十六进制值,例如"0011223344" 即 0x00、0x11 等。

如何将这些值添加到 char 数组中?

相当于说:

char array[4] = { 0x00, 0x11 ... };

【问题讨论】:

    标签: c arrays char hex


    【解决方案1】:

    你不能将 5 个字节的数据放入一个 4 字节的数组中;这会导致缓冲区溢出。

    如果字符串中有十六进制数字,则可以使用sscanf() 和循环:

    #include <stdio.h>
    #include <ctype.h>
    
    int main()
    {
        const char *src = "0011223344";
        char buffer[5];
        char *dst = buffer;
        char *end = buffer + sizeof(buffer);
        unsigned int u;
    
        while (dst < end && sscanf(src, "%2x", &u) == 1)
        {
            *dst++ = u;
            src += 2;
        }
    
        for (dst = buffer; dst < end; dst++)
            printf("%d: %c (%d, 0x%02x)\n", dst - buffer,
                   (isprint(*dst) ? *dst : '.'), *dst, *dst);
    
        return(0);
    }
    

    请注意,打印以零字节开头的字符串需要小心;大多数操作在第一个空字节处终止。请注意,此代码没有以空值终止缓冲区;目前尚不清楚是否需要空终止,并且我声明的缓冲区中没有足够的空间来添加终端空(但这很容易修复)。如果代码被打包为子例程,则很有可能需要返回转换后的字符串的长度(尽管您也可以争辩说它是源字符串的长度除以 2)。

    【讨论】:

    • 这个答案为我节省了几个小时!!!工作的。也适用于 Arduino,只需省略 printf 部分
    【解决方案2】:

    我会做这样的事情;

    // Convert from ascii hex representation to binary
    // Examples;
    //   "00" -> 0
    //   "2a" -> 42
    //   "ff" -> 255
    // Case insensitive, 2 characters of input required, no error checking
    int hex2bin( const char *s )
    {
        int ret=0;
        int i;
        for( i=0; i<2; i++ )
        {
            char c = *s++;
            int n=0;
            if( '0'<=c && c<='9' )
                n = c-'0';
            else if( 'a'<=c && c<='f' )
                n = 10 + c-'a';
            else if( 'A'<=c && c<='F' )
                n = 10 + c-'A';
            ret = n + ret*16;
        }
        return ret;
    }
    
    int main()
    {
        const char *in = "0011223344";
        char out[5];
        int i;
    
        // Hex to binary conversion loop. For example;
        // If in="0011223344" set out[] to {0x00,0x11,0x22,0x33,0x44}
        for( i=0; i<5; i++ )
        {
            out[i] = hex2bin( in );
            in += 2;
        }
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      如果字符串是正确的并且不需要保留其内容,那么我会这样做:

      #define hex(c) ((*(c)>='a')?*(c)-'a'+10:(*(c)>='A')?*(c)-'A'+10:*(c)-'0') 
      
      void hex2char( char *to ){
        for(char *from=to; *from; from+=2) *to++=hex(from)*16+hex(from+1);
        *to=0;
      }
      

      编辑 1:抱歉,我忘记用字母 A-F (a-f) 进行计算

      编辑 2:我尝试编写更迂腐的代码:

      #include <string.h> 
      
      int xdigit( char digit ){
        int val;
             if( '0' <= digit && digit <= '9' ) val = digit -'0';
        else if( 'a' <= digit && digit <= 'f' ) val = digit -'a'+10;
        else if( 'A' <= digit && digit <= 'F' ) val = digit -'A'+10;
        else                                    val = -1;
        return val;
      }
      
      int xstr2str( char *buf, unsigned bufsize, const char *in ){
        if( !in ) return -1; // missing input string
      
        unsigned inlen=strlen(in);
        if( inlen%2 != 0 ) return -2; // hex string must even sized
      
        for( unsigned i=0; i<inlen; i++ )
          if( xdigit(in[i])<0 ) return -3; // bad character in hex string
      
        if( !buf || bufsize<inlen/2+1 ) return -4; // no buffer or too small
      
        for( unsigned i=0,j=0; i<inlen; i+=2,j++ )
          buf[j] = xdigit(in[i])*16 + xdigit(in[i+1]);
      
        buf[inlen/2] = '\0';
        return inlen/2+1;
      }

      测试:

      #include <stdio.h> 
      
      char buf[100] = "test";
      
      void test( char *buf, const char *s ){
         printf("%3i=xstr2str( \"%s\", 100, \"%s\" )\n", xstr2str( buf, 100, s ), buf, s );
      }
      
      int main(){
        test( buf,      (char*)0   );
        test( buf,      "123"      );
        test( buf,      "3x"       );
        test( (char*)0, ""         );
        test( buf,      ""         );
        test( buf,      "3C3e"     );
        test( buf,      "3c31323e" );
      
        strcpy( buf,    "616263"   ); test( buf, buf );
      }

      结果:

       -1=xstr2str( "test", 100, "(null)" )
       -2=xstr2str( "test", 100, "123" )
       -3=xstr2str( "test", 100, "3x" )
       -4=xstr2str( "(null)", 100, "" )
        1=xstr2str( "", 100, "" )
        3=xstr2str( "", 100, "3C3e" )
        5=xstr2str( "", 100, "3c31323e" )
        4=xstr2str( "abc", 100, "abc" )
      

      【讨论】:

      • 这假设您被允许修改字符串,并在原地进行翻译,并且 null 终止转换后的字符串。由于第一个字节为空,您可能需要返回转换后的字符数。
      • 你说得对,但问题没有制定要求,所以这段代码已经足够好了;-)
      • 您可能还需要考虑支持 9 以上的十六进制数字。如果唯一需要工作的字符串是问题中给出的字符串,那么显然最简洁的答案是char array[] = {0, 17, 34, 51, 68};。但我认为当提问者说“即”时,他实际上是指“例如”
      【解决方案4】:

      我一直在寻找同样的东西,在阅读了很多之后,终于创建了这个功能。有人认为它可能会有所帮助

      // in = "63 09  58  81" 
      void hexatoascii(char *in, char* out, int len){
          char buf[5000];
          int i,j=0;
          char * data[5000];
          printf("\n size %d", strlen(in));
          for (i = 0; i < strlen(in); i+=2)
          {
              data[j] = (char*)malloc(8);
              if (in[i] == ' '){
                  i++;
              }
              else if(in[i + 1] == ' '){
                  i++;
              }
              printf("\n %c%c", in[i],in[i+1]);
              sprintf(data[j], "%c%c", in[i], in[i+1]);
              j++;
          }
      
          for (i = 0; i < j-1; i++){
              int tmp;
              printf("\n data %s", data[i] );
              sscanf(data[i], "%2x", &tmp);
              out[i] = tmp;
          }
          //printf("\n ascii value of hexa %s", out);
      }
      

      【讨论】:

        【解决方案5】:

        假设这是一个 little-endian ascii 平台。 也许OP的意思是“char数组”而不是“string”.. 我们使用成对的 char 和 bit masking..注意 x16 的 shiftyness..

        /* not my original work, on stacko somewhere ? */
        
        for (i=0;i < 4;i++) {
        
            char a = string[2 * i];
            char b = string[2 * i + 1];
        
            array[i] = (((encode(a) * 16) & 0xF0) + (encode(b) & 0x0F));
         }
        

        并且定义了函数 encode()...

        unsigned char encode(char x) {     /* Function to encode a hex character */
        /****************************************************************************
         * these offsets should all be decimal ..x validated for hex..              *
         ****************************************************************************/
            if (x >= '0' && x <= '9')         /* 0-9 is offset by hex 30 */
                return (x - 0x30);
            else if (x >= 'a' && x <= 'f')    /* a-f offset by hex 57 */
                return(x - 0x57);
            else if (x >= 'A' && x <= 'F')    /* A-F offset by hex 37 */
                return(x - 0x37);
        }
        

        这种方法在别处流传,它不是我的原创作品,但它很古老。 纯粹主义者不喜欢它,因为它是不可移植的,但扩展将是微不足道的。

        【讨论】:

        • 请解释一下,你是怎么知道hex(0x30,0x57,0x37)那个提取过程的?
        • 对于 ASCII,这些是连续的,(这不适用于 EBCDIC,需要更多测试。)请参阅任何 ASCII 表。 '0' 是 48。从 '0'(字符)中减去 48 得到 0(整数)。 48 是 0x30 (3x16) + (0x1)。是的,它们应该是十进制值,对不起。如果我真的很懒,我只会使用 MySQL 中的 unhex(),或者“利用”该来源。
        【解决方案6】:

        致命的地板...

        有几种方法可以做到这一点...首先,您可以使用 memcpy() 将确切的表示复制到 char 数组中。

        您也可以使用位移位和位掩码技术。我猜这是你需要做的,因为这听起来像是一个家庭作业问题。

        最后,您可以使用一些花哨的指针间接复制您需要的内存位置。

        这里详细介绍了所有这些方法:

        Store an int in a char array?

        【讨论】:

        • 您想澄清一下 memcpy() 的使用方式吗?
        【解决方案7】:

        给出一个最好的方法:

        十六进制字符串转数值,即str[] = "0011223344"转数值0x0011223344,使用

        value = strtoul(string, NULL, 16); // or strtoull()
        

        完成。如果需要删除开头的 0x00,请参见下文。

        尽管对于 LITTLE_ENDIAN 平台,加上: 十六进制值到 char 数组,值 0x11223344 到 char arr[N] = {0x00, 0x11, ...}

        unsigned long *hex = (unsigned long*)arr;
        *hex = htonl(value);
        // you'd like to remove any beginning 0x00
        char *zero = arr;
        while (0x00 == *zero) { zero++; }
        if (zero > arr) memmove(zero, arr, sizeof(arr) - (zero - arr));
        

        完成。

        注意事项: 要在 32 位系统上将长字符串转换为 64 位 hex char arr,您应该使用 unsigned long long 而不是 unsigned long,并且 htonl 是不够的,所以如下自己做,因为可能没有 htonll、htonq 或 hton64等:

        #if __KERNEL__
            /* Linux Kernel space */
            #if defined(__LITTLE_ENDIAN_BITFIELD)
                #define hton64(x)   __swab64(x)
            #else
                #define hton64(x)   (x)
            #endif
        #elif defined(__GNUC__)
            /* GNU, user space */
            #if __BYTE_ORDER == __LITTLE_ENDIAN 
                #define hton64(x)   __bswap_64(x)
            #else
                #define hton64(x)   (x)
            #endif
        #elif 
                 ...
        #endif
        
        #define ntoh64(x)   hton64(x)
        

        http://effocore.googlecode.com/svn/trunk/devel/effo/codebase/builtin/include/impl/sys/bswap.h

        【讨论】:

        • 支持的最大十六进制字符串长度:当开始 char 不是 '0' 时为 16 个字节/字符。
        【解决方案8】:
        {
            char szVal[] = "268484927472";
            char szOutput[30];
        
            size_t nLen = strlen(szVal);
            // Make sure it is even.
            if ((nLen % 2) == 1)
            {
                printf("Error string must be even number of digits %s", szVal);
            }
        
            // Process each set of characters as a single character.
            nLen >>= 1;
            for (size_t idx = 0; idx < nLen; idx++)
            {
                char acTmp[3];
                sscanf(szVal + (idx << 1), "%2s", acTmp);
                szOutput[idx] = (char)strtol(acTmp, NULL, 16);
            }
        }
        

        【讨论】:

          【解决方案9】:

          我知道的最好方法:

          int hex2bin_by_zibri(char *source_str, char *dest_buffer)
          {
            char *line = source_str;
            char *data = line;
            int offset;
            int read_byte;
            int data_len = 0;
          
            while (sscanf(data, " %02x%n", &read_byte, &offset) == 1) {
              dest_buffer[data_len++] = read_byte;
              data += offset;
            }
            return data_len;
          }
          

          函数返回保存在dest_buffer中的转换字节数。 输入字符串可以包含空格和混合大小写字母。

          "01 02 03 04 ab Cd eF 垃圾 AB"

          转换为 dest_buffer 包含 01 02 03 04 ab cd ef

          还有 "01020304abCdeFgarbageAB"

          像以前一样翻译。

          解析在第一个“错误”处停止(非十六进制,非空格)。

          注意:这也是一个有效的字符串:

          "01 2 03 04 ab Cd eF 垃圾 AB"

          并产生:

          01 02 03 04 ab cd ef

          【讨论】:

            【解决方案10】:

            下面是我的hex2binbin2hex 实现。

            这些功能:

            • 属于公共领域(随意复制和粘贴)
            • 很简单
            • 正确(即经过测试)
            • 执行错误处理(-1 表示无效的十六进制字符串)

            hex2bin

            static char h2b(char c) {
                return '0'<=c && c<='9' ? c - '0'      :
                       'A'<=c && c<='F' ? c - 'A' + 10 :
                       'a'<=c && c<='f' ? c - 'a' + 10 :
                       /* else */         -1;
            }
            
            int hex2bin(unsigned char* bin,  unsigned int bin_len, const char* hex) {
                for(unsigned int i=0; i<bin_len; i++) {
                    char b[2] = {h2b(hex[2*i+0]), h2b(hex[2*i+1])};
                    if(b[0]<0 || b[1]<0) return -1;
                    bin[i] = b[0]*16 + b[1];
                }
                return 0;
            }
            

            bin2hex

            static char b2h(unsigned char b, int upper) {
                return b<10 ? '0'+b : (upper?'A':'a')+b-10;
            }
            
            void bin2hex(char* hex, const unsigned char* bin, unsigned int bin_len, int upper) {
                for(unsigned int i=0; i<bin_len; i++) {
                    hex[2*i+0] = b2h(bin[i]>>4,   upper);
                    hex[2*i+1] = b2h(bin[i]&0x0F, upper);
                }
            }
            

            【讨论】:

              【解决方案11】:

              首先,您的问题不是很准确。字符串是std::string 还是char 缓冲区?在编译时设置?

              动态记忆几乎肯定是您的答案。

              char* arr = (char*)malloc(numberOfValues);
              

              然后,您可以遍历输入,并将其分配给数组。

              【讨论】:

              • 它不是 std::string - 这是 C 并且只有一种字符串。
              • @Paul:您认为您的回答有什么用处?问题是关于“如何遍历输入并将其分配给数组”......您小心地将其留给读者作为练习。
              • @jon:(1)C 和 C++ 有时会感到困惑,(2) 我通常将作业问题留给提问者,并且 (3) 我是期待 OP 的回应,以便磨练给出更好的答案,但他做了一个“询问并运行”。
              猜你喜欢
              • 2018-05-17
              • 2012-05-30
              • 2011-10-01
              • 1970-01-01
              • 2015-05-22
              • 2015-05-22
              • 2011-03-25
              相关资源
              最近更新 更多