use*_*189 11 c++ file-io png fstream ifstream
任何给定PNG文件的总体布局如下所示:
文件头:8字节签名.

Chunks:数据块,从图像属性到实际图像本身.
我想用C++读取PNG文件而不使用任何外部库.我想这样做是为了更深入地理解PNG格式和C++编程语言.
我开始使用fstream逐字节读取图像,但我无法通过任何PNG文件的标头.我尝试使用read( char*, int )将字节放入char数组,但read在标头之后的每个字节都失败.
如上所示,我认为我的程序总是被文件结束1A字节所占用.我正在使用Windows 7开发Windows 7和Linux机器.
#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: " << std::strlen( data ) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出总是类似于:
Attempting to open image.png
File size: 1768222
Data size: 0
Run Code Online (Sandbox Code Playgroud)
文件大小正确,但数据大小明显不正确.请注意,我尝试跳过标题(避免文件结尾字符),并在声明大小时也考虑到这一点char* data.
以下是相应修改file.seekg( ... );行时的一些数据大小值:
file.seekg( n ); data size
---------------- ---------
0 8
1 7
2 6
... ...
8 0
9 0
10 0
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
我基本上只修改了这Data size:条线.需要注意的是,该Data size:行的输出总是非常接近type我所投射的最大值file.tellg().
您的(新)代码包含两个基本错误:
data = new char[ size - 8 + 1 ];
file.seekg( 8 ); // skip the header
file.read( data, size ); // <-- here
data[ size ] = '\0'; // <-- and here
Run Code Online (Sandbox Code Playgroud)
首先,您想读取没有 8 字节前缀的数据,并分配适当的空间量(不是真的,请参阅进一步)。但此时,size仍然保存了文件的总字节数,包括 8 字节前缀。由于您要求读取size字节并且只剩下size-8字节,因此file.read操作失败。您不会检查错误,因此您不会注意到此时file已失效。通过错误检查,您应该已经看到了:
if (file)
std::cout << "all characters read successfully.";
else
std::cout << "error: only " << file.gcount() << " could be read";
Run Code Online (Sandbox Code Playgroud)
因为file从那时起就无效了,所有诸如您以后的操作都file.tellg()返回-1。
第二个错误是data[size] = '\0'。你的缓冲区没有那么大;应该是data[size-8] = 0;。目前,您正在写入超出您之前分配的内存的内存,这会导致未定义行为并可能导致以后出现问题。
但是最后一个操作清楚地表明您正在考虑字符串。PNG 文件不是字符串,而是二进制数据流。分配+1其大小并将此值设置为0(使用不必要的“字符”思维方式,使用'\0')仅当输入文件是字符串类型时才有用 - 例如,纯文本文件。
对当前问题的简单修复是这样的(好吧,并为所有文件操作添加错误检查):
file.read( data, size-8 );
Run Code Online (Sandbox Code Playgroud)
但是,我强烈建议您首先查看更简单的文件格式。PNG 文件格式紧凑且文档齐全;但它也是通用的、复杂的,并且包含高度压缩的数据。对于初学者来说太难了。
从更简单的图像格式开始。ppm是一种刻意简单的格式,很好的开始。tga,古老而简单,向您介绍了更多的概念,例如位深度和颜色映射。微软bmp有一些很好的小警告,但仍然可以被认为是“初学者友好的”。如果您对简单压缩感兴趣,a 的基本运行长度编码pcx是一个很好的起点。掌握之后,您可以查看gif使用更难的 LZW 压缩的格式。
只有当您成功地为这些实现解析器时,您才可能想再次查看 PNG。
如果您想知道从文件中读取了多少数据,则只需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;
Run Code Online (Sandbox Code Playgroud)
您读取数据的代码也存在错误。您正在读取的size文件size大小比您需要的多 8 个字节,因为您跳过了标头。正确的代码是
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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
20643 次 |
| 最近记录: |