C ++读取二进制文件

3
我希望能够了解在C++中如何读取二进制文件。以下是我的代码:
int main() {
    ifstream ifd("input.png",ios::binary |ios::ate);
    int size = ifd.tellg();
    ifd.seekg(0,  ios::beg);
    vector<char> buffer;
    buffer.reserve(size);
    ifd.read(buffer.data(), size);

    cout << buffer.data();
    return 0;
}

我认为如果我用cout输出我的缓冲区,我会得到二进制结果,但事实并非如此。
我的输出是: ˙Ř˙á6Exif
如果我读取文本文件,则以正常形式显示文本,而不是二进制。显然,我的逻辑在这里是错误的。如何将文件读入缓冲区,使其包含二进制值?
顺便说一句,我正在为实现Shannon-Fano算法而做这个,如果有人对读取二进制文件有任何建议,我将不胜感激。

你的控制台不会打印二进制数据,它会尝试将二进制数据解释为文本加上一些控制码。 - Galik
你打开了一个图片文件 image.png,其中(大概)包含二进制编码的图像数据(也就是红/绿/蓝强度级别的数组或类似的东西)。 - Galik
1
@MarkoMlakar 是的,数据是二进制的。在C++中,实际上没有区分二进制或文本。通过使用ios::binary打开文件,可以确保不会发生任何文本转换。 - Pavel P
听起来你可能误解了这里的“二进制”的含义。如果你能提供一个你错误的假设或期望的例子,我们就可以更好地回答你的问题。 - Lightness Races in Orbit
3个回答

5

您需要调整矢量的大小,而不是仅保留它:

int main()
{
    ifstream ifd("input.png", ios::binary | ios::ate);
    int size = ifd.tellg();
    ifd.seekg(0, ios::beg);
    vector<char> buffer;
    buffer.resize(size); // << resize not reserve
    ifd.read(buffer.data(), size);

    cout.write(buffer.data(), buffer.size()); // you cannot just output buffer to cout as the buffer won't have '\0' ond-of-string terminator
}

否则,您的代码将尝试在空缓冲区中读取 size 个字符。您可以使用设置向量大小的向量构造函数:vector<char> buffer(size);
您可以通过以下方式输出缓冲区的字节值:
void dumpbytes(const vector<char>& v)
{
    for (int i=0; i<v.size(); ++i)
    {
        printf("%u ", (unsigned char)v[i]);
        if ((i+1) % 16 == 0)
            printf("\n");
    }
    printf("\n");
}

或者像常见的十六进制编辑器一样输出十六进制内容:
void dumphex(const vector<char>& v)
{
    const int N = 16;
    const char hex[] = "0123456789ABCDEF";
    char buf[N*4+5+2];
    for (int i = 0; i < v.size(); ++i)
    {
        int n = i % N;
        if (n == 0)
        {
            if (i)
                puts(buf);
            memset(buf, 0x20, sizeof(buf));
            buf[sizeof(buf) - 2] = '\n';
            buf[sizeof(buf) - 1] = '\0';
        }
        unsigned char c = (unsigned char)v[i];
        buf[n*3+0] = hex[c / 16];
        buf[n*3+1] = hex[c % 16];
        buf[3*N+5+n] = (c>=' ' && c<='~') ? c : '.';
    }
    puts(buf);
}

带有“Hello World!”数据的缓冲区将打印如下:

48 65 6C 6C 6F 20 57 6F 72 6C 64 21                  Hello World!

谢谢你的回答,但结果是一样的。 - mrNobody
@MarkoMlakar 如果你的文件包含二进制数据,为什么要尝试将该数据输出到 cout?尝试读取文本文件,你会看到你的文本。 - Pavel P
@MarkoMlakar 就是这样做的!如果您的文件包含值为[20, 20, 20, 20]的4个字节,则您的代码将输出四个空格,因为' ' == 20。 “cout the bits that make that image” - 您是指十六进制还是二进制?无论哪种方式,您都需要手动转换数据。关键是,通过我的更改,您的代码应该可以正确读取文件。 - Pavel P
我该如何输出缓冲区的ASCII值? - mrNobody
@MarkoMlakar 我添加的 dumphex 会以十六进制输出您的缓冲区。我的缓冲区的ASCII值 - 对于缓冲区来说并不存在这样的东西。ASCII 是用于文本的。您是指缓冲区字节的十进制值吗?然后循环遍历您的缓冲区并打印每个字节(不要忘记强制转换为 int,否则您将打印 ASCII 字符,与您最初开始的一样)。 - Pavel P
显示剩余2条评论

2
根据Pavel的回答,你还可以添加以下内容以查看实际的二进制数据,即01。不要忘记包括bitset头文件。
void dumpbin(const vector<char>& v)
{
    for (int i = 0; i < v.size(); ++i)
    {
        cout <<bitset<8>((unsigned char)(v[i])) << " ";
        if ((i + 1) % 8 == 0)
            printf("\n");
    }
}

1
在二进制模式下打开文件意味着您的操作系统不会自动将CR / LF / CRLF格式之间的换行符进行翻译。这对计算机打印字符串的方式没有任何影响,即使是七行后。我不知道“以二进制形式获得结果”的含义,但建议按其十六进制对组表示逐个打印其组成字节的vector内容。
std::cout << std::hex << std::setfill('0');
for (const auto byte : buffer)
   std::setw(2) << byte;

输出将会看起来像这样:

0123456789abcdef0123456789abcdef

每两个字符代表您的数据中一个字节的0-255字节值,使用基数为16(或“十六进制”)的数字系统。这是非文本信息的常见表示形式。
或者,您可以以基数为2的方式输出数据(字面意思是“二进制”)。
如何呈现信息由您决定。文件打开模式与您的向量无关。
您还需要修复向量的大小;此时调用.reserve时您应该使用.resize

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接