【问题标题】:From uint8_t to hex string array?从 uint8_t 到十六进制字符串数组?
【发布时间】:2021-08-22 09:31:48
【问题描述】:

我知道这是一个简单的问题,但我完全迷失了。 我输入了一个字符串:

input: 220209

我想要:

output: uint8_t myarr[8]={0x22,0x02,0x09,0x00, 0x00, 0x00,0x00,0x00}

所以 myarr 的维度总是 8,如果我有一个较低的输入字符串,我必须用 0x00 完成。有人可以给我任何想法吗? 很抱歉打扰您,我们将不胜感激。

问候

【问题讨论】:

  • 你的标题是倒着的,根据你的例子,你想将一个十六进制字符串转换成一个字节数组。

标签: c string type-conversion hex


【解决方案1】:

希望对你有用

memset(myarr, 0, 8);
int szLen = strlen(szInput);
for(int i = 0; i < szLen / 2; i++) {
   uint8_t high = toupper(szInput[i*2]);
   uint8_t low = toupper(szInput[i*2 + 1]);

   if(high >= '0' && high <= '9')
       high = high - '0';
   if(low >= '0' && low <= '9')
       low = low - '0';
   if(high >= 'A' && high <= 'F')
       high = high - 'A' + 10;
   if(low >= 'A' && low <= 'F')
       low = low - 'A' + 10;
   myarr[i] = (high << 4) | low; // bit operation is faster than arithmetic
}

【讨论】:

  • (a) 没有解释的代码不是一个好的答案。 (b) &lt;&lt; 8 + 是错误的,因为 + 的优先级高于 &lt;&lt;。 (c) &lt;&lt; 8 是错误的移位量。
  • 我对
  • (d) 这不支持9以上的十六进制数字。
  • @kevin94 你应该使用*16,对应&lt;&lt; 4
【解决方案2】:

使用strtol()一次转换2个字符,显式使用base 16很容易:

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

// Assume s has an even number of hexdigit chars (at most 16) followed by a nul
void convert(const char *s, uint8_t out[static 8]) {
  int i = 0;
  // Handle 2 chars at a time
  while (*s) {
    char byte[3] = { *s, *(s + 1), 0 };
    out[i++] = strtol(byte, NULL, 16);
    s += 2;
  }
  // Fill the rest of the array with nuls
  for (; i < 8; i += 1) {
    out[i] = 0;
  }
}

int main(void)
{
  const char *input = "220209";
  uint8_t myarr[8];

  convert(input, myarr);

  // Pretty-print the array using hex values
  fputs("uint8_t myarr[8] = { ", stdout);
  for (int i = 0; i < 8; i += 1) {
    printf("0x%02" PRIx8, myarr[i]);
    if (i < 7) {
      fputs(", ", stdout);
    }
  }
  puts(" };");

  return 0;
}

【讨论】:

  • while (*s) ( ... s += 2; } 有 50% 的机会直接跳过 '\0' 字符串终止符。
  • 提供与预期格式不匹配的输入肯定会导致问题。如果这是 OP 可能遇到的情况,那么使其更健壮对他们来说将是一个好习惯。
【解决方案3】:

另一种选择是使用sscanf() 一次解析两个字符:

void convert(const char *s, uint8_t out[8]) 
{
    memset(out, 0, 8);
    while (sscanf(s, "%2"SCNx8, out++) == 1) {
        s += 2;
    }
}

第一行利用 64 位算法将 8 个字节设置为 0。然后我们从字符串 s 中扫描两个字符,从十六进制 ASCII 表示中读取一个 uint8_t 类型的整数。如果读取成功,我们将转到接下来的两个字符。

看起来像 code golf 提交,但很好,因为没有任何内容被复制。

另一个版本可能是:

void convert(const char *s, uint8_t out[8])
{
    static char tbl[] = {
        0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0,
        0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, 
        0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0,
        0, 1, 2, 3, 4, 5, 6,7,8,9,0,0,0,0,0,0,
        0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,
        0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0,
        0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,
        0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0,
    };
    
    memset(out, 0, 8);
    while (*s) {
        *out++ = tbl[s[0]] * 16 + tbl[s[1]];
        s += 2;
    }
}

【讨论】:

  • *(uint64_t*)out = 0; 是一个严格的别名违规,如果没有正确对齐也可能是 UB。
  • @AndrewHenle 感谢您指出这一点。您能否为我提供一个很好的参考链接,以了解更多信息?我承认“严格别名”是我不太熟悉的东西。
  • @AndrewHenle 使用memset() 修复了严格的别名冲突。
猜你喜欢
  • 2020-11-15
  • 2010-10-04
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 2012-05-30
  • 2013-08-09
  • 2013-10-13
相关资源
最近更新 更多