Arduino:将十六进制字符串转换为uint64或十进制字符串

3
我希望能将十六进制字符串(如“43a2be2a42380”)转换为它们的十进制表示,使用 uint64 变量。我需要这样做是因为我正在实现一个作为键盘的 RFID 读写器,并且按键需要是十进制数字。
我已经看到其他的答案(在 arduino 中将十六进制字符串转换为十进制),并且尝试使用 strtoul 实现解决方案,但它仅适用于32位整数,strtoull 不可用。
uint64_t res = 0;
String datatosend = String("43a2be2a42380");
char charBuf[datatosend.length() + 1];
datatosend.toCharArray(charBuf, datatosend.length() + 1) ;
res = strtoul(charBuf, NULL, 16);

我该如何在Arduino上获取一个大的十六进制字符串 / 字节数组的十进制数?


1
在uint64变量中的十进制表示。认知电路超载,检测到术语矛盾。它要么是十进制表示,要么是uint64变量。 - n. m.
我之所以这样说是因为我需要真正的十六进制数字,而不是每个数字的十进制值! - h3ct0r
3个回答

6

...使用strtoul解决方案,但它只适用于32位整数,而strtoull不可用。

使用strtoul()两次,一次用于低四个字节,一次用于其余部分,然后将两个结果相加,同时在此之前将后者乘以0x100000000LLU


那个乘法不正确。此外,使用位运算符可能更清晰:(uint64_t)upperPart << 32 | lowerPart - user694733
@user694733:谢谢 :} - alk
1
乘法更容易出错:应该是“0x100000000”。 - user694733
1
@user694733:什么看起来更“清晰”取决于读者。编译器并不关心。 - alk
@user694733:谢谢^2;已修复! - alk
有点困惑... ;}} - alk

5
您可以自己实现:

您可以自己编写实现代码:

#include <stdio.h>
#include <stdint.h>
#include <ctype.h>

uint64_t getUInt64fromHex(char const *str)
{
    uint64_t accumulator = 0;
    for (size_t i = 0 ; isxdigit((unsigned char)str[i]) ; ++i)
    {
        char c = str[i];
        accumulator *= 16;
        if (isdigit(c)) /* '0' .. '9'*/
            accumulator += c - '0';
        else if (isupper(c)) /* 'A' .. 'F'*/
            accumulator += c - 'A' + 10;
        else /* 'a' .. 'f'*/
            accumulator += c - 'a' + 10;

    }

    return accumulator;
}

int main(void)
{
    printf("%llu\n", (long long unsigned)getUInt64fromHex("43a2be2a42380"));
    return 0;
}

1
也许使用 switch ... case 会更快,而不是使用 if 的方式。 - Boiethios
虽然可能在Arduino上工作正常,但代码至少会在代码审查中引发5个问题。 - chux - Reinstate Monica
  1. "%llu\n" 匹配 unsigned long long,不一定是 uint64_t。 3) 如果 str[i]<0(而不是 EOF),则 isxdigit(str[i]) 是未定义的行为。 4) 使用 c >= 'A' && c <= 'F' 而不是 isupper() 看起来很奇怪。(同样适用于 c >= 'a' && c <= 'f')5) 不报告错误。(溢出、错误字符)6) 小问题:c - 'A' + 10 取决于通常的字符编码。另一方面,使用了 size_t 很好,尽管代码可以简单地使用 strstr++
- chux - Reinstate Monica
@chux 这只是一个例子,我假设输入是正确的。如果OP想在他的代码中检查错误,他可以这样做。然而:1)在他的64位平台上,%llu匹配uint64_t。2)你的意思是什么?在我的函数中输入是const的。3)我不会再实现另一个isxdigit,因为这里没有这个问题。我只提供一个例子。4)我不明白你的意思。5)和3的答案一样。6)我敢你找到一个ASCII码的A、B、C、D、E、F不是“连续”的平台。 - Boiethios
@chux,不过我承认Alf的答案更好。 - Boiethios
显示剩余4条评论

0

更短的转换:

void str2hex(uint8_t * dst, uint8_t * str, uint16_t len)
{
    uint8_t byte;
    while (len) {
        byte = *str-(uint8_t)0x30;
        if (byte > (uint8_t)0x9) byte -= (uint8_t)0x7;
        *dst = byte << 4;
        str++;
        byte = *str-(uint8_t)0x30; 
        if (byte > (uint8_t)0x9) byte -= (uint8_t)0x7;
        *dst |= byte;
        str++; dst++;
        len -= (uint16_t)0x1;
    }
}

然后从无符号字符缓冲区获取uint64_t:

uint64_t hex2_64(uint8_t * ptr)
{
    uint64_t ret;
    ret = (uint64_t)((uint64_t)(*ptr) << (uint64_t)56); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)48); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)40); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)32); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)24); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)16); ptr++;
    ret |= (uint64_t)((uint64_t)(*ptr) << (uint64_t)8); ptr++;
    ret |= (uint64_t)(*ptr);
    return ret;
}

str2hex()?这个更好被称为str2uint8(),不是吗? - alk
为什么要传递一个指向 hex2_64() 的指针(最好称为 uint8x8array2uint64())? - alk
1
*ptr << 56 失败。 *ptr 将被提升为 int 然后进行移位操作。如果 int 小于 64 位,则结果是未定义的。如果 int 是 64 位,则结果是实现定义的。 - chux - Reinstate Monica
*str-(uint8_t)0x30;byte > (uint8_t)0x9 中的强制类型转换没有任何作用。 - chux - Reinstate Monica
@chux,我无时无刻不在推广类型转换 - 这是我的偏执症 =)我被ST SPL源代码感染了。 - imbearr
显示剩余2条评论

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