Эффективная обработка и чтение файла траектории с использованием ООП - PullRequest
1 голос
/ 18 мая 2019

Я пишу код для чтения в больших файлах .xyz.Файлы этого типа полезны для визуализаторов Molecular Dynamics, таких как VMD.Таким образом, формат файла выглядит примерно так:

#Number of particles
#frame number
#Coordinates

В качестве примера:

5
0
C    1.23    2.33    4.56
C    1.23    2.33    5.56
C    1.23    2.33    6.56
C    1.23    2.33    7.56
C    1.23    2.33    8.56
5
1
C    2.23    2.33    4.56
C    2.23    3.33    5.56
C    2.23    4.33    6.56
C    2.23    5.33    7.56
C    2.23    6.33    8.56

и так далее.Я пытался понять этот пост здесь https://codereview.stackexchange.com/questions/201743/processing-xyz-data-from-a-large-file, в котором говорится об эффективном чтении из больших наборов данных с использованием метода перегрузки операторов.Я пытаюсь написать класс, который может читать такие большие файлы траекторий и давать мне следующие выходные данные: 1) количество частиц 2) общее количество кадров 3) набор координат на каждом временном шаге.Поэтому я попытался записать следующее на основе этого поста, чтобы прочитать в формате файла, как упомянуто выше.Пока что приведенный ниже код способен читать один кадр и после этого завершается.

#include <iostream>
#include <vector>
#include <fstream>

struct Particle{

    long double x,y,z;
    char tab ='\t';
    char newline = '\n';
    char atom ;
    friend std::istream& operator>>(std::istream& in, Particle &xyz) {
        in >> xyz.atom >> xyz.x >> xyz.y >> xyz.z ;
        return in;
    }
    friend std::ostream& operator<<(std::ostream& out, Particle &xyz){
        out << xyz.x << xyz.tab << xyz.y << xyz.tab << xyz.z << xyz.newline;
        return out;
    }
};
class XYZ_frame_read
{

    int curr_frame;
    int num_particles;
    std::vector<Particle> coordinates_t;

    public:

    friend std::istream& operator>>(std::istream& in, XYZ_frame_read &traj ){

                in >> traj.num_particles;
                in >> traj.curr_frame;
                Particle p;
                while(in >> p){
                    traj.coordinates_t.push_back(p);
                }
            return in;
        }
    friend std::ostream& operator<<(std::ostream& out, XYZ_frame_read &traj){

            for(int i = 0; i< traj.num_particles ;i ++){
                out << traj.coordinates_t.at(i) ;
            }
            return out;
        }
};

int main(int argc, char *argv[]){

    std::ifstream in(argv[1]);
    XYZ_frame_read* frames = new XYZ_frame_read[3];
    in >> frames[0];
    std::cout << frames[0];

    return 0;
}

Проблема в том, что я не понимаю, как я буду реализовывать этот метод для чтения следующих кадров и продолжения их добавленияк вектору coordinates_t для каждого экземпляра объекта XYZ_frame_read.Я думаю, я понимаю, как это работает, так что очевидно, что while(!in.eof()) не может быть и речи, поскольку он будет просто читать первый кадр снова и снова.Я новичок в C ++ и работаю над проектом, связанным с молекулярной дианамикой, любые изменения / предложения приветствуются!Спасибо за помощь!

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

Я пытался использовать

size_t i = 0;
while(in >> frames[i]){
    std::cout << frames[i];
    if(i == 3){
        break;
    }
    i++;
}

Возвращается пустым.Не работаетЦикл даже не выполняется.

Ответы [ 2 ]

0 голосов
/ 19 мая 2019

Вы очень близки, вам просто нужно проверить свой ввод в перегруженных функциях оператора, и не использовать new !, вместо этого просто используйте std::vector<XYZ_frame_read> frames;

Например, с вашей перегрузкойistream для class XYZ_frame_read все, что вам нужно:

    friend std::istream& operator>>(std::istream& in, XYZ_frame_read &traj)
    {
        /* validate that num_particles and curr_frame read */
        if (in >> traj.num_particles >> traj.curr_frame) {
            int n = traj.num_particles; /* set number of particles to read */
            Particle p;

            while (n-- && (in >> p))    /* read that number of particles */
                traj.coordinates_t.push_back(p);
        }
        return in;
    }

Затем в main() вместо выделения с new для frames, как вы делаете здесь:

    XYZ_frame_read* frames = new XYZ_frame_read[3];
    in >> frames[0];
    std::cout << frames[0];

просто используйте std::vector<XYZ_frame_read> frames;, а затем используйте временный class XYZ_frame_read, чтобы проверить чтение перед добавлением его в вектор кадров, например,

    std::vector<XYZ_frame_read> frames; /* vector of frames (NO new!) */

    for (;;) {                      /* continual loop while good input */
        XYZ_frame_read tmp;         /* temp XYZ_frame_read for read */

        if ((in >> tmp))            /* if read is good */
            frames.push_back(tmp);  /* add it to vector of frames */
        else
            break;                  /* otherwise -- bail */
    }

Для вывода просто используйтепетля с автонастройкой for, например

    for (auto & f : frames)         /* auto-ranged for loop to output frames */
        std::cout << "\nframe: " << f.get_frame() << 
                    "  particles: " << f.get_nparticles() << "\n\n" << 
                    f << '\n';

В целом, вы получите:

#include <iostream>
#include <vector>
#include <fstream>

struct Particle {

    long double x,y,z;
    char tab ='\t';
    char newline = '\n';
    char atom ;

    friend std::istream& operator>>(std::istream& in, Particle &xyz) {
        in >> xyz.atom >> xyz.x >> xyz.y >> xyz.z;
        return in;
    }

    friend std::ostream& operator<<(std::ostream& out, Particle &xyz) {
        out << xyz.x << xyz.tab << xyz.y << xyz.tab << xyz.z << xyz.newline;
        return out;
    }
};

class XYZ_frame_read
{
    int curr_frame;
    int num_particles;
    std::vector<Particle> coordinates_t;

  public:

    friend std::istream& operator>>(std::istream& in, XYZ_frame_read &traj)
    {
        /* validate that num_particles and curr_frame read */
        if (in >> traj.num_particles >> traj.curr_frame) {
            int n = traj.num_particles; /* set number of particles to read */
            Particle p;

            while (n-- && (in >> p))    /* read that number of particles */
                traj.coordinates_t.push_back(p);
        }
        return in;
    }
    friend std::ostream& operator<<(std::ostream& out, XYZ_frame_read &traj) {

        for(int i = 0; i< traj.num_particles ;i ++)
            out << traj.coordinates_t.at(i) ;

        return out;
    }
    int get_frame(void) { return curr_frame; }
    int get_nparticles (void) { return num_particles; }
    int getpsize(void) { return coordinates_t.size(); }
};

int main(int argc, char *argv[]) {

    std::ifstream in(argv[1]);
    std::vector<XYZ_frame_read> frames; /* vector of frames (NO new!) */

    for (;;) {                      /* continual loop while good input */
        XYZ_frame_read tmp;         /* temp XYZ_frame_read for read */

        if ((in >> tmp))            /* if read is good */
            frames.push_back(tmp);  /* add it to vector of frames */
        else
            break;                  /* otherwise -- bail */
    }

    for (auto & f : frames)         /* auto-ranged for loop to output frames */
        std::cout << "\nframe: " << f.get_frame() << 
                    "  particles: " << f.get_nparticles() << "\n\n" << 
                    f << '\n';

    return 0;
    (void)argc;     /* suppress -Wunused warning */
}

Существует огромное количество преимуществ использования vector для class XYZ_frame_readвместо выделения с new.Автоматическое управление памятью - это только верхушка айсберга.

Пример входного файла

Использование вашего примера ввода:

$ cat particles.txt
5
0
C    1.23    2.33    4.56
C    1.23    2.33    5.56
C    1.23    2.33    6.56
C    1.23    2.33    7.56
C    1.23    2.33    8.56
5
1
C    2.23    2.33    4.56
C    2.23    3.33    5.56
C    2.23    4.33    6.56
C    2.23    5.33    7.56
C    2.23    6.33    8.56

Пример Использование / Вывод

Просто укажите имя файла, и ваш std::vector<XYZ_frame_read> frames будет автоматически заполнен, независимо от того, сколько кадров присутствует в вашем файле данных (до пределов вашей виртуальной памяти)

$ ./bin/paticle particles.txt

frame: 0  particles: 5

1.23    2.33    4.56
1.23    2.33    5.56
1.23    2.33    6.56
1.23    2.33    7.56
1.23    2.33    8.56


frame: 1  particles: 5

2.23    2.33    4.56
2.23    3.33    5.56
2.23    4.33    6.56
2.23    5.33    7.56
2.23    6.33    8.56
0 голосов
/ 18 мая 2019

while(!in.eof()) не может быть и речи, потому что eof так не работает.

Почему iostream :: eof внутри условия цикла (то есть `while (! Stream.eof)()) `) считается неправильным?

Я не уверен, что вижу проблему, что не так с

size_t i = 0;
while (in >> frames[i])
    ++i;

(кроме возможности ошибок границ массива).

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

Этот код неверен

 friend std::istream& operator>>(std::istream& in, XYZ_frame_read &traj) {
     in >> traj.num_particles;
     in >> traj.curr_frame;
     Particle p;
     while(in >> p){
          traj.coordinates_t.push_back(p);
     }
     return in;
 }

Это говорит о том, что продолжайте читать частицы до тех пор, пока не произойдет сбой чтения.Это неправильно, вы знаете, сколько существует частиц.Стоит сказать, что продолжайте читать частицы, пока вы не прочитаете num_particles из них (или чтение не удастся).Т.е. следует сказать

 friend std::istream& operator>>(std::istream& in, XYZ_frame_read &traj) {
     in >> traj.num_particles;
     in >> traj.curr_frame;
     Particle p;
     for (int i = 0; i < traj.num_particles && in >> p; ++i) 
          traj.coordinates_t.push_back(p);
     }
     return in;
 }
...