You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二进制文件读入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);
}

问题根源

  1. 测试文件写入错误:create_test_file_hex直接把字符串"40"写入二进制文件,实际写入的是ASCII字符'4'(十进制52)和'0'(十进制48),而非十六进制值0x40(十进制64)——这是得到52的直接原因。
  2. read_int函数未初始化result:int result;无初始值,第一次执行result*=255会触发未定义行为。
  3. 字节计算逻辑错误:多字节整数拼接时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 18:20:05