在 C++ 中将十六进制字符串转换为无符号字符
Convert hex string into unsigned char in C++
我想将字符串中的十六进制表示形式转换为无符号字符变量,如下所示:
std::stringstream ss;
uint8_t x;
ss << "1f";
ss >> std::hex >> x; // result: x = 0x31 (=49 in decimal and ='1' as char)
显然,我假设转换会导致 x = 0x1f(十进制 =31),因为 0x1f 小于 0xff,这是可以存储在 8 位无符号字符中的最大值。
相反发生的是在转换中只使用了我的字符串的前 8 位。
有人可以向我解释为什么会发生这种情况以及如何解决它吗?
std::uint8_t
是(通常见下文)unsigned char
的别名,corresponding operator>>
将其视为字符类型而不是整数类型。正因为如此,字符'1'
被读入x,它的ASCII值为49。那'1'
的ASCII值的十六进制表示恰好是你要解析的值的十进制表示是巧合;尝试解析 "1e"
或 "10"
或 "1xyz"
仍然会导致 x == 49
.
要解决此问题,请先解析为另一种整数类型,然后缩小为 8 位:
std::stringstream ss;
uint8_t x;
unsigned tmp;
ss << "1f";
ss >> std::hex >> tmp;
x = tmp; // may need static_cast<uint8_t>(tmp) to suppress
// compiler warnings.
迂腐的附录(主要是历史意义)
如果我们完全迂腐,uint8_t
是一个可选的 (!) 实现定义的无符号整数类型,如果存在,它正好是 8 位宽。 C++在[cstdint.syn]/2中将定义推迟到C标准,而C99在7.18.1.1中定义:
1 The typedef name intN_t
designates a signed integer type with width N, no padding bits, and a two's complement representation. Thus, int8_t
denotes a signed integer type with a width of exactly 8 bits.
2 The typedef name uintN_t
designates an unsigned integer type with width N. Thus, uint24_t
denotes an unsigned integer type with a width of exactly 24 bits.
3 These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, it shall define the corresponding typedef names.
这件事的背景是历史。曾几何时,存在一个字节没有 8 位的平台,例如许多 PDP(更不用说早期的 UNIVACs1 等十进制计算机)。今天我们很少对这些感兴趣,但在设计 C 时它们很重要,因此,如果今天开发 C,可能会做出某些假设,但 C 标准中没有。
在这些平台上,无法始终轻松提供 8 位整数类型,并且 unsigned char
被定义为恰好一个字节宽,如果一个字节是不是 8 位宽。这一点,连同其他一些东西 2,就是为什么所有 uintN_t
类型都是可选的,也是为什么其中 none 与特定整数类型相关联的原因。目的是定义提供特定低级行为的类型。如果实现不能提供那种行为,至少它会出错而不是编译废话。
因此,完全迂腐:如果您完全使用 uint8_t
,则可以编写一个完全拒绝您的代码的符合标准的 C++ 实现。也可以编写一个符合规范的实现,其中 uint8_t
是一个不同于 unsigned char
的整数类型,其中问题中的代码可以正常工作。
然而,实际上,您不太可能遇到这样的实现。我所知道的所有当前 C++ 实现都将 uint8_t
定义为 unsigned char
.3
的别名
1 即使那还不是兔子洞的深度,尽管我怀疑 C 语言的创造者是否考虑过 Setun(俄罗斯平衡三元计算机)。
2 例如,并非所有这些机器都将整数表示为二进制补码。
3如果您知道没有的,请发表评论,我会在此处记录下来。我想可能有一个微控制器工具包有理由偏离。
我想将字符串中的十六进制表示形式转换为无符号字符变量,如下所示:
std::stringstream ss;
uint8_t x;
ss << "1f";
ss >> std::hex >> x; // result: x = 0x31 (=49 in decimal and ='1' as char)
显然,我假设转换会导致 x = 0x1f(十进制 =31),因为 0x1f 小于 0xff,这是可以存储在 8 位无符号字符中的最大值。 相反发生的是在转换中只使用了我的字符串的前 8 位。
有人可以向我解释为什么会发生这种情况以及如何解决它吗?
std::uint8_t
是(通常见下文)unsigned char
的别名,corresponding operator>>
将其视为字符类型而不是整数类型。正因为如此,字符'1'
被读入x,它的ASCII值为49。那'1'
的ASCII值的十六进制表示恰好是你要解析的值的十进制表示是巧合;尝试解析 "1e"
或 "10"
或 "1xyz"
仍然会导致 x == 49
.
要解决此问题,请先解析为另一种整数类型,然后缩小为 8 位:
std::stringstream ss;
uint8_t x;
unsigned tmp;
ss << "1f";
ss >> std::hex >> tmp;
x = tmp; // may need static_cast<uint8_t>(tmp) to suppress
// compiler warnings.
迂腐的附录(主要是历史意义)
如果我们完全迂腐,uint8_t
是一个可选的 (!) 实现定义的无符号整数类型,如果存在,它正好是 8 位宽。 C++在[cstdint.syn]/2中将定义推迟到C标准,而C99在7.18.1.1中定义:
1 The typedef name
intN_t
designates a signed integer type with width N, no padding bits, and a two's complement representation. Thus,int8_t
denotes a signed integer type with a width of exactly 8 bits.2 The typedef name
uintN_t
designates an unsigned integer type with width N. Thus,uint24_t
denotes an unsigned integer type with a width of exactly 24 bits.3 These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, it shall define the corresponding typedef names.
这件事的背景是历史。曾几何时,存在一个字节没有 8 位的平台,例如许多 PDP(更不用说早期的 UNIVACs1 等十进制计算机)。今天我们很少对这些感兴趣,但在设计 C 时它们很重要,因此,如果今天开发 C,可能会做出某些假设,但 C 标准中没有。
在这些平台上,无法始终轻松提供 8 位整数类型,并且 unsigned char
被定义为恰好一个字节宽,如果一个字节是不是 8 位宽。这一点,连同其他一些东西 2,就是为什么所有 uintN_t
类型都是可选的,也是为什么其中 none 与特定整数类型相关联的原因。目的是定义提供特定低级行为的类型。如果实现不能提供那种行为,至少它会出错而不是编译废话。
因此,完全迂腐:如果您完全使用 uint8_t
,则可以编写一个完全拒绝您的代码的符合标准的 C++ 实现。也可以编写一个符合规范的实现,其中 uint8_t
是一个不同于 unsigned char
的整数类型,其中问题中的代码可以正常工作。
然而,实际上,您不太可能遇到这样的实现。我所知道的所有当前 C++ 实现都将 uint8_t
定义为 unsigned char
.3
1 即使那还不是兔子洞的深度,尽管我怀疑 C 语言的创造者是否考虑过 Setun(俄罗斯平衡三元计算机)。
2 例如,并非所有这些机器都将整数表示为二进制补码。
3如果您知道没有的,请发表评论,我会在此处记录下来。我想可能有一个微控制器工具包有理由偏离。