C++ 如何对字符串进行高效十六进制编码

作者:袖梨 2026-06-19
std::hex不能直接用于字符串编码,因其仅为I/O流操纵器,仅作用于整数输出;字符串需手动逐字节转十六进制,且必须用unsigned char强转以防符号扩展越界。

为什么 std::hex 不能直接用于字符串编码

因为 std::hex 是 I/O 流操纵器,只影响整数类型的格式化输出,对 std::stringchar 数组无作用。直接写 std::cout 会输出地址(隐式转为指针),不是你想要的每个字节的十六进制表示。

std::format(C++20)最简洁安全

C++20 的 std::format 支持逐字节格式化,自动处理宽度、大小写和分隔,且不依赖流状态,线程安全:

std::string hex_encode(const std::string& s) {    std::string out;    out.reserve(s.size() * 2); // 预分配避免多次 realloc    for (unsigned char c : s) {        out += std::format("{:02x}", c); // 小写、两位、补零    }    return out;}
  • {:02x}:固定宽度 2,小写十六进制,不足补零;换成 {:02X} 得大写
  • 不推荐 std::format("{}", c) —— 默认不补零,单字节可能输出 "a" 而非 "0a"
  • 若项目未启用 C++20,std::format 不可用,需降级方案

兼容 C++11 的手动查表法最快

查表法避免格式化开销,也绕过 std::stringstream 的构造/析构成本,实测比流式方法快 3–5 倍:

static constexpr char hex_chars[] = "0123456789abcdef";std::string hex_encode(const std::string& s) {    std::string out(s.size() * 2, ' ');    for (size_t i = 0; i < s.size(); ++i) {        unsigned char c = static_cast<unsigned char>(s[i]);        out[2*i]     = hex_chars[c >> 4];        out[2*i + 1] = hex_chars[c & 0x0F];    }    return out;}
  • 必须用 unsigned char 强转:否则负值 char(如 xFF)会符号扩展成大整数,查表越界
  • 预设 out 容量并用空格初始化,避免 push_back 动态增长开销
  • 大写表用 "0123456789ABCDEF",别混用大小写

std::stringstream 容易踩的坑

虽可工作,但默认行为极易出错:

立即学习“C++免费学习笔记(深入)”;

std::stringstream ss;ss << std::hex << std::setfill('0');for (unsigned char c : s) {    ss << std::setw(2) << static_cast<int>(c);}return ss.str();
  • 漏掉 std::setfill('0') → 补空格而非 '0'
  • 漏掉 std::setw(2) → 单字节输出 "a" 而非 "0a"
  • 漏掉 static_cast<int>(c)</int> → 输出字符本身(如 '@')而非其 ASCII 码
  • 流状态(std::hex, std::setw)是全局的,多线程下需加锁或改用局部流

查表法没有状态污染,也没有隐式类型转换风险,复杂场景下更值得优先考虑。

相关文章

精彩推荐