如何在c++中从pgm文件中读取数据

How to read in data from a pgm file in C++

本文关键字:文件 读取 数据 pgm 中从 c++      更新时间:2023-10-16

到目前为止,我可以读取每行并将其打印到控制台:

void readFile(){
   string line;
   ifstream myfile("example1.pgm");
   if (myfile.is_open()){
       while (myfile.good()){
         getline (myfile,line);
         cout << line;
       }
   }

然而,pgm文件显然总是在数据开始前有以下内容:

P2
# test.pgm
24 7
15

我如何调整我的代码,使它检查"P2"是存在的,忽略任何注释(#),并存储变量和随后的像素数据?

我有点迷路了,不熟悉c++,所以任何帮助都很感激。

谢谢

解析文件有很多不同的方法。对于这样的问题,你可以看看这个网站上的答案。个人而言,我会使用getline()循环并测试/解析每一行(存储在变量"line"中),您也可以使用stringstream,因为它更容易与多个值一起使用:

<<h2>想法/h2>

第一行:测试P2 (Portable graymap)是否存在,可能使用类似

的内容
if(line.compare("P2")) ...

第二行:什么都不做,你可以继续下一个getline()

第三行:存储图像的大小;对于stringstream,你可以这样做

int w,h;
ss >> w >> h;

以下行:存储像素数据,直到到达文件末尾

生成的代码

你可以试试下面的代码,并根据你的需要修改它:

#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();
}

编辑

简化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");

这处理注释(如果存在)和PBM的特殊情况(没有'maxsample'),并且无论是否在输入流上启用异常,它都可以工作。

一旦你读了头,读取图像数据通常是一件简单的事情,因为格式被定义为只是一个顺序的数据转储(可能是ASCII或二进制取决于'magic'值)。在16位二进制编码样本的情况下,格式规范指出"最重要的字节是第一个"(大端),因此这种情况可能需要一些特定于平台的处理。

如前所述,这需要c++ 11——可能是因为我使用stringstream作为临时的方式。

一个警告:在一个病态的情况下,这可能会浪费大量的时间/RAM,而试图读取一个无效的头——因为getline调用不是固有的边界。有一个相对简单的解决方案(用更健壮的东西替换getline),但它需要更多的代码。

对于生产质量的应用程序,考虑使用libnetpbm。