如何从 C++ 中的 pgm 文件中读取数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8126815/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to read in data from a pgm file in C++
提问by ostegg548
So far I can read every line and print it out to the console:
到目前为止,我可以读取每一行并将其打印到控制台:
void readFile(){
string line;
ifstream myfile("example1.pgm");
if (myfile.is_open()){
while (myfile.good()){
getline (myfile,line);
cout << line;
}
}
However a pgm file apparently will always have the following at the start before the data:
然而,一个 pgm 文件显然在数据之前的开头总是有以下内容:
P2
# test.pgm
24 7
15
How can i adapt my code so that it checks that "P2" is present, ignores any comments (#), and stores the variables and subsequent pixel data?
我如何调整我的代码,以便它检查“P2”是否存在,忽略任何注释 (#),并存储变量和后续像素数据?
I'm a bit lost and new to c++ so any help is appreicated.
我对 C++ 有点迷茫和陌生,所以任何帮助都得到了认可。
Thanks
谢谢
回答by BenC
There are a lot of different ways to parse a file. For something like this, you could look at the answers on this site. Personally, I would go with a loop of getline() and test/parse every line (stored in the variable "line"), you can also use a stringstream since it is easier to use with multiple values :
解析文件有很多不同的方法。对于这样的事情,您可以查看此站点上的答案。就我个人而言,我会使用 getline() 循环并测试/解析每一行(存储在变量“line”中),您也可以使用 stringstream,因为它更容易与多个值一起使用:
Idea
主意
First line :test that P2 (Portable graymap) is present, maybe with something like
第一行:测试 P2(便携式灰度图)是否存在,也许使用类似的东西
if(line.compare("P2")) ...
Second line :do nothing, you can go on with the next getline()
第二行:什么都不做,你可以继续下一个 getline()
Third line :store the size of the image; with a stringstream you could do this
第三行:存储图片的大小;使用 stringstream 你可以做到这一点
int w,h;
ss >> w >> h;
Following lines :store the pixel data until you reach the end of the file
以下几行:存储像素数据,直到到达文件末尾
Resulting code
结果代码
You can try this code and adapt it to your needs :
您可以尝试使用此代码并根据您的需要进行调整:
#include <iostream> // cout, cerr
#include <fstream> // ifstream
#include <sstream> // stringstream
using namespace std;
int main() {
int row = 0, col = 0, numrows = 0, numcols = 0;
ifstream infile("file.pgm");
stringstream ss;
string inputLine = "";
// First line : version
getline(infile,inputLine);
if(inputLine.compare("P2") != 0) cerr << "Version error" << endl;
else cout << "Version : " << inputLine << endl;
// Second line : comment
getline(infile,inputLine);
cout << "Comment : " << inputLine << endl;
// Continue with a stringstream
ss << infile.rdbuf();
// Third line : size
ss >> numcols >> numrows;
cout << numcols << " columns and " << numrows << " rows" << endl;
int array[numrows][numcols];
// Following lines : data
for(row = 0; row < numrows; ++row)
for (col = 0; col < numcols; ++col) ss >> array[row][col];
// Now print the array to see the result
for(row = 0; row < numrows; ++row) {
for(col = 0; col < numcols; ++col) {
cout << array[row][col] << " ";
}
cout << endl;
}
infile.close();
}
EDIT
编辑
Here is a good tutorial on how to use stringstreams.
这是一个关于如何使用 stringstreams的好教程。
回答by nobar
A way of simplifying PNM(PBM/PGM/PPM) header processing is to build up a header string line-by-line until you have captured all of the requisite data. It doesn't take too much code to do this using only the standard C++ libraries...
一种简化PNM(PBM/PGM/PPM) 标头处理的方法是逐行构建标头字符串,直到您捕获了所有必需的数据。只使用标准的 C++ 库就不需要太多的代码......
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
...
std::string header, magic;
int width=0, height=0, maxsample=0, samples=0, bits=0, bytes=0;
do {
try { getline(is,magic); } catch ( const std::ios_base::failure & ) {}
if ( !magic.empty() && magic[0] != '#' ) header += magic+" ";
if ( !( std::stringstream(header+" 1") >> magic >> width >> height >> maxsample ).eof() ) break;
if ( ( (magic=="P1"||magic=="P4") && maxsample==1 ) || !is.good() ) break;
} while ( true );
samples = magic=="P1"?1:magic=="P2"?1:magic=="P3"?3:magic=="P4"?1:magic=="P5"?1:magic=="P6"?3:0;
bits = (magic=="P1"||magic=="P4")?1:maxsample<256?8:maxsample<256*256?16:0, bytes = (width*samples*bits+7)>>3;
if ( width<=0 || height<=0 || maxsample<=0 || samples<=0 || bits<=0 ) throw std::runtime_error("invalid PNM header");
This handles comments (if present) and the special-case of PBM (no 'maxsample') -- and it works regardless of whether or not exceptions are enabled on the input stream.
这会处理注释(如果存在)和 PBM 的特殊情况(没有“maxsample”)——无论是否在输入流上启用异常,它都可以工作。
Once you've read the header, reading the image data is usually a simple matter since the format is defined to just be a sequential data dump (which may be either ASCII or binary depending on the 'magic' value). In the case of 16-bit binary-encoded samples, the format specification indicates that "The most significant byte is first" (big endian), so this case may require some platform-specific handling.
一旦您阅读了标题,读取图像数据通常是一件简单的事情,因为格式被定义为只是顺序数据转储(根据“魔术”值,可能是 ASCII 或二进制)。在 16 位二进制编码样本的情况下,格式规范指示“最高有效字节在前”(大端),因此这种情况可能需要一些特定于平台的处理。
As written, this requires C++11 -- probably due to the way I'm using stringstream
as a temporary.
正如所写,这需要 C++11——可能是由于我stringstream
作为临时使用的方式。
One caveat: In a pathological case, it is possible for this to waste a lot of time/RAM while attempting to read an invalid header -- since the getline
call isn't inherently bounded. There's a relatively simple solution (replace getline
with something more robust), but it requires a bit more code.
一个警告:在病态的情况下,这可能会在尝试读取无效标头时浪费大量时间/RAM——因为getline
调用本身并不是有界的。有一个相对简单的解决方案(用更强大的东西替换getline
),但它需要更多的代码。
For production-quality applications, consider using libnetpbm.
对于生产质量的应用程序,请考虑使用libnetpbm。