将 unsigned int 中的原始字节转换为字符串的最简单和最有效的方法

Simplest and most efficient way to turn raw bytes in an unsigned int to a string

在 C++ 中,将 unsigned int 中的原始字节转换为字符串(例如通过 ASCII 编码读取)的最简单和最有效的方法是什么?

一个uint64_t包含8个字节,我想将这些字节解释为(char*),附加终止标志('\0'),并转换为字符串。这样我就可以将“原始”数据存储在尽可能短的字符串中。

例如,假设我们有包含位 0100 0001 0100 0001 的 uint16_t(我相信这里每个 8 位序列都是 'A')。我想将这些放在字符串 str 中,使得 (str == "AA") 为真。

我想问的是如何在不压缩的情况下以最 space 高效的方式在字符串中存储一些数据,例如 int。

非常感谢。

如果你只想将8个二进制字节解释为一个字符串,你可以这样做:

uint64_t val = 0xAAAAAAAA;  // or whatever
char buf[sizeof(val)+1];
memcpy(buf, &val, sizeof(buf));
buf[sizeof(val)] = '[=10=]';  // apply NUL terminator
const std::string s(buf);

请注意,如果 val 中的任何字节为 0,则由于 C 字符串的 NUL 终止语义,您的字符串将在该字节处终止。

请注意 sizeof(string) 在我的系统上是 24 (MacOS/X);在其他平台上它可能是 16,但它可能永远不会是 8 或更少,这意味着在任何情况下您都不会通过将 uint64 存储为 std::string 来节省内存。

我会在下面留下我原来的答案,但最有效的解决方案似乎也是最简单的(由@Remy Lebeau 在下面提出):

std::string string(reinterpret_cast<const char *>(&value), sizeof(value));

如果您使用的是 C++17,您还可以使用 std::string_view 并完全避免复制和堆分配:

std::string_view string(reinterpret_cast<const char *>(&value), sizeof(value));

当然,所有这些都围绕着 C 字符串表示的非常简单的性质。我们在这里所做的就是告诉语言将整数的地址解释为 C 字符串。如果你真的关心 space 和执行效率并且不能使用 C++17 那么也许只使用 C 字符串。

原解:

此解决方案仍然受到字节序考虑的影响,但鉴于所有主要体系结构都是小端,这应该只在网络上下文中考虑。 std::string 可以接受字节迭代器。它会自动添加空终止符。

uint64_t value = 100;
const uint8_t *const bytes = reinterpret_cast<const uint8_t *>(&value);
std::string string(bytes, bytes + sizeof(uint64_t));

就 'fastest' 而言,这取决于您的体系结构,例如,对于英特尔,它可能是这样的:https://godbolt.org/z/9n6cz6Wss

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

void u64_to_str(uint64_t n, char str[17]) {
  // a few constants
  const __m128i _9 = _mm_set1_epi8(9);
  const __m128i ascii0 = _mm_set1_epi8('0');
  const __m128i asciiA = _mm_set1_epi8('A' - 10);
  const __m128i x0F0F = _mm_set1_epi16(0x0F0F);
  const __m128i swizzle_mask = _mm_set_epi8(
      8, 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7);

  // split each byte into a nibble (wrong byte ordering here)
  __m128i b = _mm_and_si128(x0F0F, _mm_set_epi64x(n, n >> 4));

  // are we greater than 9?
  __m128i cmp = _mm_cmpgt_epi8(b, _9);

  // select correct ascii value to add! 
  __m128i acsii_offset = _mm_blendv_epi8(ascii0, asciiA, cmp);

  // convert nibbles to ascii values to 0 -> 'F'
  b = _mm_add_epi8(b, acsii_offset);

  // shuffle to correct ordering
  _mm_storeu_si128((__m128i*)str, _mm_shuffle_epi8(b, swizzle_mask));

  // terminate string
  str[16] = 0;
}

如果您只是想要一种方法将字节快速转换为字符串,同时坚持标准 C/C++(无需分配 std::string 的内容),那么只需执行以下操作:

void u8_to_string(uint8_t a, char str[2]) {
  char hex[] = "0123456789ABCDEF";
  str[0] = hex[a >> 4];
  str[1] = hex[a & 0xF];
}