在C++开发中,我们常常遇到这样的需求:从日志、网络包或硬件接口中拿到一段十六进制文本,它实际对应着一张图片的二进制内容,但因为缺失文件头或头部损坏,无法直接当作图片打开。此时可以通过解析十六进制字节流,手动补齐或重构二进制文件头,再将完整数据写入文件来恢复图片。

一、核心处理思路
要把十六进制字节流保存为图片并完成文件头重构,主要分三步:首先将十六进制字符串每两个字符转成一个字节;其次根据目标图片格式构造正确的文件头,例如BMP以424D开头,PNG以89504E470D0A1A0A开头;最后把文件头和图像数据字节依次写入二进制文件。
1. 十六进制转二进制
标准做法是用 strtol 或自定义映射表,把字符对如 "FF" 转为 0xFF。注意十六进制串长度应为偶数,且不包含 0x 前缀。
2. 文件头重构
如果原始流已含数据区但无头,只需在前面插入对应格式的头字节;如果头损坏,则按格式规范重写头部。下面以BMP为例,其最简文件头为两个字节 42 4D。
二、完整示例代码
以下代码演示将十六进制字符串(含BMP数据但缺头)重构并保存为 .bmp 文件:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
// 十六进制字符转十进制
int hexCharToInt(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return 0;
}
// 十六进制字符串转字节流
std::vector<unsigned char> hexToBytes(const std::string& hex) {
std::vector<unsigned char> bytes;
for (size_t i = 0; i < hex.size(); i += 2) {
int high = hexCharToInt(hex[i]);
int low = hexCharToInt(hex[i + 1]);
bytes.push_back((high << 4) | low);
}
return bytes;
}
int main() {
// 假设这是缺BMP文件头的十六进制像素数据
std::string hexData = "0000000000000000";
std::vector<unsigned char> body = hexToBytes(hexData);
// 重构BMP文件头 42 4D
std::vector<unsigned char> header = {0x42, 0x4D};
std::vector<unsigned char> out;
out.insert(out.end(), header.begin(), header.end());
out.insert(out.end(), body.begin(), body.end());
std::ofstream f("output.bmp", std::ios::binary);
if (f.is_open()) {
f.write(reinterpret_cast<const char*>(out.data()), out.size());
f.close();
std::cout << "图片已保存" << std::endl;
}
return 0;
}
三、注意事项
- 写文件时必须用二进制模式 std::ios::binary,否则换行符可能被转换导致数据错误。
- 不同图片格式头结构差异大,PNG、JPG 不能简单用两字节头,需补全长头再接数据。
- 若原始流本身含错乱头,应先剔除再重构,避免头部重复。
四、小结
通过上面的方法,我们可以用C++轻松将十六进制字节流还原成图片。关键点是正确解析十六进制、了解目标格式文件头规范,并用二进制方式写盘。实际项目中可据此扩展支持多格式自动识别与批量恢复。