二进制文件读入uint8数组转十进制整数结果错误排查
二进制文件整数解析问题:十六进制40转为十进制52而非64的排查与解决
我在解析二进制文件、提取uint8/int8到uint64等各类整数时遇到问题。为做通用处理,我从文件指针读取数据到uint8数组缓存,但测试时发现,文件里的十六进制值40本该转成十进制64,结果始终得到52。试过多种字节读取赋值的方法都没用,怀疑是字节序问题?
我的read_int方法
int read_int(FILE * file,int n,bool is_signed) throw(){ assert(n>0); uint8_t n_chars[n]; int result; for (int i = 0; i < n; i++) { if(fread(&n_chars[i],sizeof(n_chars[i]),1,file)!=1){ std::cerr<< "fread() failed!\n"; throw new ReadOpFailed(); } result*=255; result+=n_chars[i]; } std::cout<< "int read: "<<result<<"\n"; return result; //-------------Some ideas that didn't work out either------------------ // std::stringstream ss; // ss << std::hex << static_cast<int>(static_cast<unsigned char>(n_chars)); // Convert byte to hexadecimal string // int result; // ss >> result; // Parse the hexadecimal string to integer // std::cout << "result" << result<<"\n"; }
测试代码
struct TestContext{ FILE * create_test_file_hex(char * input_hex,const char * rel_file_path = "test.gguf") { std::ofstream MyFile(rel_file_path, std::ios::binary); // Write to the file MyFile << input_hex; // Close the file MyFile.close(); // std::fstream outfile (rel_file_path,std::ios::trunc); // char str[20] = // outfile.write(str, 20); // outfile.close(); FILE *file = fopen(rel_file_path,"rb"); try{ assert(file != nullptr); }catch (int e){ std::cout << "file couldn't be opened due to exception n° "<<std::to_string(e)<<"\n"; ADD_FAILURE(); } std::remove(rel_file_path); //remove file whilst open, to be able to use it, but delete it after the last pointer was deleted. return file; } }; TEST(test_tool_functions, test_read_int){ int n = 1; // little endian if true if(*(char *)&n == 1) {std::cout<<"Little Endian Detected!!!\n";} else{std::cout<<"Big Endian Detected!!!\n";} std::string file_hex_content = "400A0E00080000016"; uint64_t should; std::istringstream("40") >> std::hex >> should; ASSERT_EQ(should,64); uint64_t result = read_int(TestContext().create_test_file_hex(file_hex_content.data()),1,false); ASSERT_EQ(result,should); }
问题根源
- 测试文件写入错误:
create_test_file_hex直接把字符串"40"写入二进制文件,实际写入的是ASCII字符'4'(十进制52)和'0'(十进制48),而非十六进制值0x40(十进制64)——这是得到52的直接原因。 - read_int函数未初始化result:
int result;无初始值,第一次执行result*=255会触发未定义行为。 - 字节计算逻辑错误:多字节整数拼接时,
result*=255应为result*=256(每个字节是2^8=256进制);另外单字节整数不存在字节序问题,你的问题和字节序无关。
修复步骤
1. 修正测试文件写入逻辑
把十六进制字符串转换成对应二进制字节写入,而非直接写字符串:
FILE* create_test_file_hex(const char* input_hex, const char* rel_file_path = "test.gguf") { std::ofstream MyFile(rel_file_path, std::ios::binary); // 十六进制字符串转二进制字节 for (size_t i = 0; i < strlen(input_hex); i += 2) { char hex_byte[3] = {input_hex[i], input_hex[i+1], '\0'}; uint8_t byte = strtol(hex_byte, nullptr, 16); MyFile.write(reinterpret_cast<const char*>(&byte), 1); } MyFile.close(); FILE* file = fopen(rel_file_path, "rb"); if (!file) { std::cerr << "Failed to open test file\n"; ADD_FAILURE(); } std::remove(rel_file_path); return file; }
2. 修复read_int函数
- 初始化
result为0 - 修正字节拼接逻辑为
result*=256(或等价的位运算) - 支持有符号/无符号类型,改用
uint64_t作为通用返回类型:
#include <cstdint> #include <cassert> #include <iostream> class ReadOpFailed {}; uint64_t read_int(FILE* file, int n, bool is_signed) { assert(n > 0 && n <= 8); // 最多支持8字节(uint64_t) uint64_t result = 0; uint8_t byte; for (int i = 0; i < n; ++i) { if (fread(&byte, sizeof(byte), 1, file) != 1) { std::cerr << "fread() failed!\n"; throw ReadOpFailed(); } result = (result << 8) | byte; // 等价于result*256 + byte,大端序解析 } // 处理有符号类型的符号位扩展 if (is_signed) { uint64_t sign_mask = 1ULL << (n * 8 - 1); if (result & sign_mask) { result |= ~((1ULL << (n * 8)) - 1); } } std::cout << "int read: " << result << "\n"; return result; }
3. 调整测试代码
确保十六进制字符串长度为偶数(原字符串末尾多了一个'6',导致长度奇数):
TEST(test_tool_functions, test_read_int){ int n = 1; if(*(char *)&n == 1) {std::cout<<"Little Endian Detected!!!\n";} else{std::cout<<"Big Endian Detected!!!\n";} std::string file_hex_content = "400A0E0008000001"; uint64_t should; std::istringstream("40") >> std::hex >> should; ASSERT_EQ(should, 64); uint64_t result = read_int(TestContext().create_test_file_hex(file_hex_content.data()), 1, false); ASSERT_EQ(result, should); }
内容的提问来源于stack exchange,提问作者Heavypilgrim
相关产品推荐
相关产品推荐

