Как читать данные из файла pgm в C ++ - PullRequest
3 голосов
/ 14 ноября 2011

Пока что я могу прочитать каждую строку и распечатать ее на консоли:

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 ++, поэтому любая помощь оценена.

Спасибо

Ответы [ 2 ]

8 голосов
/ 14 ноября 2011

Существует много разных способов разбора файла.Примерно так можно посмотреть ответы на этом сайте .Лично я бы пошел с циклом getline () и протестировал / проанализировал каждую строку (хранится в переменной "line"), вы также можете использовать stringstream, так как его проще использовать с несколькими значениями:

Идея

Первая строка: проверка наличия P2 (Portable Graymap), возможно с чем-то вроде

if(line.compare("P2")) ...

Вторая строка: ничего не делать, вы можете перейти к следующей getline ()

Третья строка: сохранить размер изображения;со строковым потоком вы можете сделать это

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

РЕДАКТИРОВАТЬ

Вот это хороший учебник о том, как использовать stringstreams .

2 голосов
/ 14 июня 2013

Способ упрощения обработки заголовка 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 в качестве временного.

Одно предостережение: в патологическом случае это может привести к потере большого количества времени / ОЗУ при попытке прочитать недопустимый заголовок - поскольку вызов getline по своей природе не ограничен , Существует относительно простое решение ( заменить getline чем-то более надежным ), но для этого требуется немного больше кода.

Для приложений производственного качества рассмотрите возможность использования libnetpbm .

...