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

使用std::ifstream二进制模式读取PNG签名仅部分正确,求排查

问题:二进制模式打开std::ifstream读取PNG签名仅部分正确,如何验证PNG有效性?

我尝试用二进制模式打开的std::ifstream读取PNG图片的文件签名,但只能读到少量正确字节。确认输入是有效PNG图片,现在需要验证PNG有效性,请问问题出在哪里?

#include <fstream>
#include <iostream>
using namespace std;

const auto PNG_SIGN_SIZE = 8;
const uint8_t PNG_SIGN_BUFFER[] = {137, 80, 78, 71, 13, 10, 26, 10};

bool validate(const string& filename) {
    ifstream ifs;
    ifs.open(filename, ios::binary);
    bool is_valid = true;

    if (ifs.is_open()) {
        uint8_t buffer[PNG_SIGN_SIZE]{};

        for (int i = 0; i < PNG_SIGN_SIZE; ++i) {
            ifs >> buffer[i];

            if (ifs.fail()) {
                printf("ERROR: Fail to read bytes.\n");
                is_valid = false;
                break;
            }
        }

        for (int i = 0; i < PNG_SIGN_SIZE; ++i) {
            // printf("%u %u\n", buffer[i], PNG_SIGN_BUFFER[i]);
            if (buffer[i] != PNG_SIGN_BUFFER[i]) {
                is_valid = false;
                break;
            }
        }

    } else {
        printf("ERROR: Could not read <%s>.\n", filename.c_str());
        is_valid = false;
    }

    ifs.close();
    return is_valid;
}

int main() {
    string filename = "baby.png";
    auto res = validate(filename);

    cout << boolalpha << res;

    return 0;
}

问题根源

核心错误在于你使用了**格式化输入运算符>>**来读取二进制字节。>>是为文本数据设计的,它会自动跳过所有空白字符(包括ASCII值为13的回车、10的换行,而这两个正好是PNG签名里的第5、6字节),并且遇到这些字符时不会将其写入buffer,导致后续读取的字节全部错位,自然无法匹配完整的PNG签名。

修复方案

处理二进制文件时,必须使用非格式化的读取方法,比如以下两种:

方案1:一次性读取所有签名字节(推荐)

用std::ifstream::read()直接读取固定长度的二进制数据,效率更高且避免循环出错:

bool validate(const string& filename) {
    ifstream ifs(filename, ios::binary);
    bool is_valid = true;

    if (ifs.is_open()) {
        uint8_t buffer[PNG_SIGN_SIZE]{};
        // 一次性读取8字节PNG签名
        ifs.read(reinterpret_cast<char*>(buffer), PNG_SIGN_SIZE);
        
        // 验证是否成功读取了全部8字节
        if (!ifs || ifs.gcount() != PNG_SIGN_SIZE) {
            printf("ERROR: Fail to read bytes.\n");
            is_valid = false;
        } else {
            // 对比签名
            for (int i = 0; i < PNG_SIGN_SIZE; ++i) {
                if (buffer[i] != PNG_SIGN_BUFFER[i]) {
                    is_valid = false;
                    break;
                }
            }
        }
    } else {
        printf("ERROR: Could not read <%s>.\n", filename.c_str());
        is_valid = false;
    }

    return is_valid;
}

方案2:逐个读取字节

如果需要逐个处理字节,可以用std::ifstream::get(),它会读取包括空白字符在内的所有字节,不会跳过:

// 在原循环中替换ifs >> buffer[i]为:
buffer[i] = static_cast<uint8_t>(ifs.get());
if (ifs.eof() || ifs.fail()) {
    printf("ERROR: Fail to read bytes.\n");
    is_valid = false;
    break;
}

额外说明

  • 打开文件时已经正确使用了ios::binary模式,这部分没问题,保证了系统不会自动转换换行符。
  • 处理二进制数据时,永远不要使用>>、<<这类格式化输入输出运算符,它们会破坏二进制数据的原始字节结构。

内容的提问来源于stack exchange,提问作者ghulam2545

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:55:13