C++将长十六进制字符串解析为std::vector<uint8_t>的异常问题
我想用C++将长十六进制字符串分割为单个字节并存入std::vector<uint8_t>。例如输入字符串09e1c5f70a65ac519458e7e53f36,期望得到的std::vector<uint8_t>内容为{0x09, 0xe1, 0xc5, 0xf7, 0x0a, 0x65, 0xac, 0x51, 0x94, 0x58, 0xe7, 0xe5, 0x3f, 0x36},输出格式应为09 e1 c5 f7....,但实际输出仅得到字节值的一半,比如0 e c f.....。不想使用Boost库,我使用的代码如下:
#include <iostream> #include <string> #include <sstream> #include <vector> #include <iomanip> int main(void) { std::string in("09e1c5f70a65ac519458e7e53f36"); size_t len = in.length(); std::vector<uint8_t> out; for(size_t i = 0; i < len; i += 2) { std::istringstream strm(in.substr(i, 2)); uint8_t x; strm >> std::hex >> x; out.push_back(x); } for (int i=0; i<out.size(); ++i) std::cout << out[i] << ' '; return 0; }
问题原因
核心问题在于uint8_t的类型特性:它本质是unsigned char的别名。当用std::istringstream配合std::hex读取时,流会将其视为字符类型而非整数类型,因此只会读取十六进制子串的第一个字符,而非解析整个两位字符串为字节值。比如读取"09"时,只会取'0'对应的ASCII值0;读取"e1"时取'e'对应的ASCII值101,最终导致输出结果不符合预期。
另外,直接用std::cout输出uint8_t时,会将其当作字符输出(比如0x09是制表符、0xe1是不可见控制字符),这也会让你看到错误的输出表现。
解决方案
方法1:用整数类型中转解析
先将十六进制子串读取到更大的整数类型(比如unsigned int),再转换为uint8_t存入容器:
#include <iostream> #include <string> #include <sstream> #include <vector> #include <iomanip> int main(void) { std::string in("09e1c5f70a65ac519458e7e53f36"); size_t len = in.length(); std::vector<uint8_t> out; for(size_t i = 0; i < len; i += 2) { std::istringstream strm(in.substr(i, 2)); unsigned int temp; strm >> std::hex >> temp; out.push_back(static_cast<uint8_t>(temp)); } // 正确输出十六进制格式 for (auto byte : out) { std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte) << ' '; } return 0; }
方法2:手动解析十六进制字符(更高效)
跳过字符串流,直接通过位运算将两位十六进制字符转换为字节,适合处理长字符串:
#include <iostream> #include <string> #include <vector> #include <iomanip> uint8_t hex_char_to_byte(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); // 非法字符处理,可根据需求抛出异常或返回默认值 return 0; } int main(void) { std::string in("09e1c5f70a65ac519458e7e53f36"); size_t len = in.length(); std::vector<uint8_t> out; out.reserve(len / 2); // 预分配空间提升效率 for(size_t i = 0; i < len; i += 2) { uint8_t byte = (hex_char_to_byte(in[i]) << 4) | hex_char_to_byte(in[i+1]); out.push_back(byte); } // 正确输出十六进制格式 for (auto byte : out) { std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte) << ' '; } return 0; }
两种方法都能正确生成目标std::vector<uint8_t>,且无需依赖Boost库。
内容的提问来源于stack exchange,提问作者Vijayakumar Sargunam

