C++ 如何手动读取C+中的PNG文件+;? 便携式网络图形概述

C++ 如何手动读取C+中的PNG文件+;? 便携式网络图形概述,c++,file-io,png,fstream,ifstream,C++,File Io,Png,Fstream,Ifstream,任何给定PNG文件的总体布局如下所示: 文件头:一个8字节的签名 块:从图像属性到实际图像本身的数据块 问题 我想在没有使用任何外部库的情况下读取C++中的PNG文件。我想这样做是为了加深对PNG格式和C++编程语言的理解。 我开始使用fstream逐字节读取图像,但我无法通过任何PNG文件的头。我尝试使用read(char*,int)将字节放入char数组,但是read在头之后的每个字节上都失败 如上所述,我认为我的程序总是被文件1Abyte的末尾所占用。我正在为Windows7和Linu

任何给定PNG文件的总体布局如下所示:

文件头:一个8字节的签名

:从图像属性到实际图像本身的数据块


问题 <>我想在没有使用任何外部库的情况下读取C++中的PNG文件。我想这样做是为了加深对PNG格式和C++编程语言的理解。 我开始使用
fstream
逐字节读取图像,但我无法通过任何PNG文件的头。我尝试使用
read(char*,int)
将字节放入
char
数组,但是
read
在头之后的每个字节上都失败

如上所述,我认为我的程序总是被文件
1A
byte的末尾所占用。我正在为Windows7和Linux机器开发Windows7


我的一些(旧)代码 文件大小正确,但数据大小显然不正确。注意,我试图跳过标题(避免文件结尾字符),并且在声明
char*data
的大小时也考虑了这一点

下面是我修改
文件时的一些数据大小值。seekg(…)相应行:

file.seekg( n );             data size
----------------             ---------
0                            8
1                            7
2                            6
...                          ...
8                            0
9                            0
10                           0

我的一些新代码
#包括
#包括
#包括
#包括
const char*INPUT_FILENAME=“image.png”;
int main()
{
std::ifstream文件;
大小\u t大小=0;

std::cout如果您想知道从文件中读取了多少数据,只需再次使用
tellg()

data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size );
data[ size ] = '\0';
if(file.good()) // make sure we had a good read.
    std::cout << "Data size: " << file.tellg() - 8 << std::endl;
解决方案1:
file.read(数据、大小);
Size\u t data\u Size=file.tellg()-8;
std::cout您的(新)代码包含两个基本错误:

因为从那时起,
file
是无效的,所以所有的操作,比如稍后的
file.tellg()
都返回
-1

第二个错误是
data[size]='\0'
。您的缓冲区没有那么大;它应该是
data[size-8]=0;
。当前,您正在向内存中写入超出先前分配的内容,这会导致未定义的行为,并可能导致以后出现问题

最后一个操作清楚地表明您是根据字符串进行思考的。PNG文件不是字符串,而是二进制数据流。为其大小分配
+1
,并将该值设置为
0
(使用不必要的“字符”思维方式,使用
'\0'
)仅当输入文件为字符串类型(例如纯文本文件)时才有用

解决当前问题的一个简单方法是(好吧,并为所有文件操作添加错误检查):

但是,我强烈建议您先看一个简单得多的文件格式。PNG文件格式紧凑且有很好的文档记录;但它也通用、复杂,并且包含高度压缩的数据。对于初学者来说,这太难了

从一个更简单的图像格式开始。是一个刻意简单的格式,很好开始。古老但简单,向您介绍了更多的概念,如位深度和颜色映射。Microsoft有一些很好的小注意事项,但仍然可以被视为“初学者友好”。如果您对简单压缩感兴趣,基本的运行长度Encodia的ng是一个很好的起点。掌握了这一点后,您可以查看该格式,该格式使用更难的LZW压缩


只有成功地实现了这些解析器,您才可能希望再次查看PNG。

strlen
在第一个空终止符处停止,您假设添加到缓冲区末尾的空终止符是唯一的一个。通常将二进制数据视为文本字符串不是一个好主意。@CaptainObvlious但如何才能获得这些数据大小那么e值呢?您所说的有点道理,但也似乎意味着根据这些值,头后面的每个值都是空终止符:file.seekg(0),数据大小:8;file.seekg(1),数据大小:7;file.seekg(2),数据大小:6;…file.seekg(8),数据大小:0;file.seekg(9),数据大小:0;file.seekg(10),数据大小:0;…我一点也不暗示。存储在PNG中的数据应该被视为二进制数据,这意味着您永远不应该假设为空终止符,并且
strlen
将是正确的方式。您需要检查PNG的文件格式,并开始解释数据的实际内容,而不是假设它是j我想在C++中读取PNG文件而不使用STL < /COD>以外的任何代码。你的代码不使用STL中的任何东西。如果它使用了,至少你可以替换<代码>新[]。
对于
std::vector
@user3745189,图像数据本身可以有空字符。图像数据中的这些空字符与字符串终止无关——它们只是存在的数据。因此,您不需要使用停止在空值上的字符串函数合并。我使用了稍微修改过的代码(
((size\t)file.tellg)()-8)
)相反,这是输出:
文件大小:1768222
数据大小:4294967287
,这让我觉得发生了一些可怕的事情,因为数据大小远远大于文件大小。它看起来像是溢出。@user3745189:这么大的数字解释为十六进制更有意义。你会看到它是一个巨大的数字,或者可能是(签名)一个小的负数…
-8
@user3745189您的代码中也有错误。我编辑了工作代码的答案。我不建议使用
readsome()
as说它可能不总是做你想做的事情。@NathanOliver。我从来没有这个问题,所以我没有意识到。谢谢你指出。你可以使用file.rdbuf()->sgetn(数据,大小):
file.seekg( n );             data size
----------------             ---------
0                            8
1                            7
2                            6
...                          ...
8                            0
9                            0
10                           0
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstddef>

const char* INPUT_FILENAME = "image.png";

int main()
{
  std::ifstream file;
  size_t size = 0;

  std::cout << "Attempting to open " << INPUT_FILENAME << std::endl;

  file.open( INPUT_FILENAME, std::ios::in | std::ios::binary | std::ios::ate );
  char* data = 0;

  file.seekg( 0, std::ios::end );
  size = file.tellg();
  std::cout << "File size: " << size << std::endl;
  file.seekg( 0, std::ios::beg );

  data = new char[ size - 8 + 1 ];
  file.seekg( 8 ); // skip the header
  file.read( data, size );
  data[ size ] = '\0';
  std::cout << "Data size: " << ((unsigned long long)file.tellg() - 8) << std::endl;
}
data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size );
data[ size ] = '\0';
if(file.good()) // make sure we had a good read.
    std::cout << "Data size: " << file.tellg() - 8 << std::endl;
const char* INPUT_FILENAME = "ban hammer.png";

int main()
{
    std::ifstream file;
    size_t size = 0;

    std::cout << "Attempting to open " << INPUT_FILENAME << std::endl;

    file.open(INPUT_FILENAME, std::ios::in | std::ios::binary);
    char* data = 0;

    file.seekg(0, std::ios::end);
    size = file.tellg();
    std::cout << "File size: " << size << std::endl;
    file.seekg(0, std::ios::beg);

    data = new char[size - 8 + 1];
    file.seekg(8); // skip the header
    file.read(data, size - 8);
    data[size] = '\0';
    std::cout << "Data size: " << file.tellg() << std::endl;
    cin.get();
    return 0;
}
file.read( data, size );
Size_t data_size = file.tellg() - 8;
std::cout << "Data size: " << data_size << std::endl;
Size_t data_size = file.readsome( data, size );
std::cout << "Data size: " << data_size << std::endl;
data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size );  // <-- here
data[ size ] = '\0';      // <-- and here
if (file)
  std::cout << "all characters read successfully.";
else
  std::cout << "error: only " << file.gcount() << " could be read";
file.read( data, size-8 );