正确做法是按每两个字符切片并校验:先去空格、检查偶数长度,再两两截取用std::stoul解析为0~0xFF范围内的字节存入vector<uint8_t>。

字符串转十六进制字节数组的常见错误
直接用 std::stoi(s, nullptr, 16) 解析整个字符串,会失败——它只处理单个整数,不是字节序列。更常见的是把 "A1B2C3" 当成一个大整数解析,结果溢出或截断。还有人用 sscanf 循环读,但没处理好长度校验和大小端,导致末尾字节错位或越界。
安全解析:按每两个字符切片 + 校验
十六进制字符串必须是偶数长度,每个字节对应两个字符(如 "FF" → 0xFF)。先检查长度,再两两截取,用 std::stoul 带基数解析,比 std::stoi 更稳妥(避免有符号溢出)。
关键点:
- 字符串去空格:用
std::erase_if(str, ::isspace)(C++20)或手动遍历过滤 - 长度校验:若
str.length() % 2 != 0,直接报错或补前导零(按需) - 每对字符调用
std::stoul(sub, nullptr, 16),结果必须 ≤ 0xFF,否则非法字符(如"G1") - 存入
std::vector<uint8_t></uint8_t>,不是int或char(避免符号扩展问题)
兼容旧标准的 C++11 写法示例
不用 C++20 的 erase_if,也不依赖异常做流程控制(比如不靠 std::stoul 抛异常来判断非法字符),可以手动遍历解析:
立即学习“C++免费学习笔记(深入)”;
std::vector<uint8_t> hexstr_to_bytes(const std::string& s) {
std::vector<uint8_t> out;
out.reserve((s.size() + 1) / 2);
for (size_t i = 0; i < s.size(); i += 2) {
if (i + 1 >= s.size()) break; // 自动跳过奇数末尾
uint8_t hi = hex_char_to_val(s[i]);
uint8_t lo = hex_char_to_val(s[i + 1]);
if (hi == 0xFF || lo == 0xFF) continue; // 跳过非法字符
out.push_back((hi << 4) | lo);
}
return out;
}
其中 hex_char_to_val 是个查表函数('0'→0, 'a'→10, 'F'→15),大小写不敏感,返回 0xFF 表示非法。
注意大小写、前缀与编码边界
真实数据常带 "0x" 前缀(如 "0xA1B2"),需提前去掉;也有人混用大小写("a1B2"),所以解析函数必须统一转小写或查表兼容。另外,如果输入来自网络或文件,要考虑 BOM 或 UTF-8 非 ASCII 字符污染——哪怕看起来是十六进制,开头几个字节可能是 EF BB BF,得先剥离。
最易被忽略的是:解析后数组长度 ≠ 原字符串长度 / 2,当存在空格、换行、前缀或非法字符时,必须显式定义“容错策略”——是跳过?报错?还是终止?没有默认正确答案,取决于你的协议规范。


















