简体   繁体   中英

Reading component vector data from text file into structure array C++

So I have a text file with 3 different components of velocity, as well as the time stamp associated with each velocity measurement. The text file can be seen here .

The u component corresponds to the x direction, v to the y direction, and w to the z direction. Now, I need to take these data points and place them into my structure for the velocity components, which is defined as:

struct cmpnts
{
   double x, y, z;
}

In the past when I did this without structures and with only one overall velocity, I just used pointers so the dynamic arrays could be created when the file was read. This has to be dynamic because the number of points in each windspeed file changes, and I can't manually redefine the value every time I use a new file.

To do this without the structures my code looked like this:

int main()
{
    int numberofpoints; // the number of data points
                        // char arrays for storing the variables names 
    char ch1[128], ch2[128];
    cout << ch1 << endl;

    // Two pointers for creating the dynamic arrays later
    double *itime, *windspeed;

    // create an object for reading a file
    ifstream imyfile;

    // open windspeed.txt
    imyfile.open("windspeed.txt");

    if (imyfile.is_open()) // check if the file is open 
    {
        // read the total number of data points
        imyfile >> numberofpoints;

        // double arrays for storing time and the velocity variables
        itime = new double[numberofpoints];
        windspeed = new double[numberofpoints];

        // read the two variable names in windspeed.txt file 
        imyfile >> ch1 >> ch2;

        // read the time and wind speed
        int i;
        for (i = 0; i<numberofpoints; i++)
        {
            imyfile >> itime[i] >> windspeed[i];

        }

        // close the file
        imyfile.close();
    }
    else
    {
        cout << "unable to open the file";
        exit(0);
    }
}

I can't seem to get this to work with my structures though. I'm certain I'm making some syntax error somewhere with the pointers (I'm new to C++ so I apologize if it's something dumb!). Is it possible to do this without pointers? My code for reading to the structures looks like this (and obviously it's not working!):

#include <iostream>
#include <fstream>
using namespace std;

struct cmpnts
{
    double x, y, z;
};

struct wind
{
    cmpnts velocity;
    cmpnts direction;
    cmpnts urms;
    double airdensity;
};

struct turbine
{
    double R, Cp, V, yaw, power;
    cmpnts direction;
};

int main()
{

    // Read data from file
    int numberofpoints;         // the number of data points
    char ch1[128], ch2[128], ch3[128], ch4[128];    // Char arrays for storing the variables names 

    // Pointers for creating the dynamic arrays later
    double *itime;
    cmpnts *uSpeed;
    cmpnts *vSpeed;
    cmpnts *wSpeed;

    // create an object for reading a file
    ifstream imyfile;

    // open windspeed.txt
    imyfile.open("windspeed.txt");

    if (imyfile.is_open()) // check if the file is open 
    {
        // read the total number of data points
        imyfile >> numberofpoints;

        // double arrays for storing time and the velocity variables
        itime = new double[numberofpoints];
        uSpeed->x = new double[numberofpoints];
        vSpeed->y = new double[numberofpoints];
        wSpeed->z = new double[numberofpoints];

        // read the two variable names in windspeed.txt file 
        imyfile >> ch1 >> ch2 >> ch3 >> ch4;

        // read the time and wind speed
        int i;
        for (i = 0; i<numberofpoints; i++)
        {
            imyfile >> itime[i] >> uSpeed[i] >> vSpeed[i] >> wSpeed[i];

        }

        // close the file
        imyfile.close();
    }
    else
    {
        cout << "unable to open the file";
        exit(0);
    }
}

Your code does the following:

cmpnts *uSpeed;

This creates a variable, uSpeed, which can hold the address of a cmpnts instance in memory. There's no real magic to pointers, they're just a variable that holds a numeric value. That value is the location of something, its address, in memory. We mark it as a pointer more as a way to try and avoid confusing variables that are values and variables whose values are addresses.

The key thing here is that this pointer is uninitialized. It could contain zero or it could contain any random garbage.

Later on, you write

uSpeed->x = new double[numberofpoints];

-> is the dereference operator. The variable on the left has to be a pointer to some type of thing, and the thing on the right is then assumed to be a member of the thing at that address.

Recall: You did not initialize uSpeed .

But there's a second problem here. cmpnts::x is a double, but you're trying to assign an address to it.

    uSpeed->x = new double[numberofpoints];
    vSpeed->y = new double[numberofpoints];
    wSpeed->z = new double[numberofpoints];

It's really not clear what you think you're doing here, but it looks like you just wanted:

    cmpnts* speed = new cmpnts[numberOfPoints];

and then

    imyfile >> itime[i] >> speed[i].x >> speed[i].y >> speed[i].z;

Working with pointers is hard. Don't do it. Use modern C++.

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

struct Vector  // note upper case for my own class
{
    double x_, y_, z_;  // the '_' distinguishes a member from a variable
};

struct SpeedEntry
{
    int time_;
    Vector vel_;
};

int main()
{
    // create an object for reading a file
    std::ifstream imyfile("windspeed.txt");
    if (!imyfile.is_open()) // check if the file is open 
    {
        std::cerr << "unable to open the file";
        return 1;  // non-zero return from main = failure
    }

    // read the total number of data points
    size_t numberOfPoints;
    imyfile >> numberofpoints;
    imyfile.ignore();  // ignore end of line/whitespace

    // for storing time and the velocity variables
    std::vector<SpeedEntry> speeds;
    speeds.reserve(numberOfPoints);

    // read the two variable names in windspeed.txt file
    // we're ignoring these values so...
    std::string vars[4];
    imyfile >> vars[0] >> vars[1] >> vars[2] >> vars[3];
    std::cout << "vars are " << vars[0] << ", " << vars[1] << ", " << vars[2] << ", " << vars[3] << "\n";

    // Now read each of the lines
    for (size_t i = 0; i < numberOfPoints; ++i)
    {
        SpeedEntry entry;
        if (!(imyfile >> entry.time_ >> entry.vel_.x_ >> entry.vel_.y_ >> entry.vel_.z_)) {
            std::cerr << "Error reading entry #" << (i+1) << "\n";
            return 2;
        }
        speeds.push_back(entry);
    }

    std::cout << "Read " << speeds.size() << " entries\n";

    // c++11 range-based loop
    for (auto&& entry : speeds)
    {
        std::cout << "Read: " << entry.time_ << " : "
                   << entry.vel_.x_ << ',' << entry.vel_.y << ',' << entry.vel.z_
                   << '\n';
    }
}  // file closes automatically when `imyfile` goes out of scope

Further reading: std::vector , std::string ,

Or a version that leverages operator<< and operator>> :

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

struct Vector  // note upper case for my own class
{
    double x_, y_, z_;  // the '_' distinguishes a member from a variable
};

// implement input and output operators for Vec
std::istream& operator >> (std::istream& in, Vector& vec)
{
    return in >> vec.x_ >> vec.y_ >> vec.z_;
}
std::ostream& operator << (std::ostream& out, const Vector& vec)
{
    return out << vec.x_ << ' ' << vec.y_ << ' ' << vec.z;
}

struct SpeedEntry
{
    int time_;
    Vector vel_;
};

// implement input and output operators for SpeedEntry
std::istream& operator >> (std::istream& in, SpeedEntry& entry)
{
    return in >> entry.time_ >> entry.vel_;
}

std::ostream& operator << (std::ostream& out, const Vector& vec)
{
    return out << entry.time_ << ' ' << entry.vel_;
}


int main()
{
    std::ifstream imyfile("windspeed.txt");
    if (!imyfile.is_open()) // check if the file is open 
    {
        std::cerr << "unable to open the file";
        return 1;  // non-zero return from main = failure
    }

    // read the total number of data points
    size_t numberOfPoints;
    imyfile >> numberofpoints;
    imyfile.ignore();  // ignore end of line/whitespace

    // read the two variable names in windspeed.txt file
    // we're ignoring these values so...
    std::string vars[4];
    imyfile >> vars[0] >> vars[1] >> vars[2] >> vars[3];
    std::cout << "vars are " << vars[0] << ", " << vars[1] << ", " << vars[2] << ", " << vars[3] << "\n";

    // for storing time and the velocity variables
    std::vector<SpeedEntry> speeds;
    speeds.reserve(numberOfPoints);

    // Now read each of the lines
    for (size_t i = 0; i < numberOfPoints; ++i)
    {
        SpeedEntry entry;
        if (!(imyfile >> entry)) {
            std::cerr << "Error reading entry #" << (i+1) << "\n";
            return 2;
        }
        speeds.push_back(entry);
    }

    std::cout << "Read " << speeds.size() << " entries\n";

    // c++11 range-based loop
    for (auto&& entry : speeds)
        std::cout << "Read: " << entry << '\n';
}  // imyfile closes automatically when it goes out of scope

--- Edit ---

A std::vector can encompass almost any type, so you can even build compound vectors:

std::vector<std::vector<std::string>> stringvecs;

this creates something like a 2d-array of strings:

std::vector<std::vector<std::string>> stringvecs;

// allocates 5 empty std::vector<strings> in stringvecs
stringvecs.resize(5);

// push "hello world" onto the first entry
stringvecs[0].push_back("hello");

At this point, the stringvecs looks like this:

stringvecs {
   [0] : std::vector of std::string containing { "hello" },
   [1] : empty std::vector of std::string
   [2] :  ""        ""     ""      ""
   [3] :  ""        ""     ""      ""
   [4] :  ""        ""     ""      ""
}

We could access "hello" by writing:

std::cout << stringvecs[0][0] << "\n";

Remember:

stringvecs is of type std::vector<std::vector<std::string>>
if stringvecs.empty() == false
  stringvecs[0] is of type std::vector<std::string> (returned by reference)
  if stringvecs.empty() == false && stringvecs[0].empty() == false
    stringvecs[0][0] is of type std::string (returned by reference)

When you are using a vector of a structure:

struct T {
    int i_;
    bool b_;
    std::string s_;
};
std::vector<T> ts;

You first need to access an instance of T within the vector to be able to access it's members:

ts.emplace_back(1, false, "first");  // creates a T with these values
ts.emplace_back(2, true, "seconds");

std::cout << ts[0].s_ << "\n";  // access member "s_" of the first entry
std::cout << ts[1].i_ << "\n";  // prints 2, array indexes are 0-based

I dug up a vector sandbox I used for someone a while back here: http://ideone.com/HERvy1

Walk through the code and match it up with the output. You can also find more about std::vector here: http://en.cppreference.com/w/cpp/container/vector

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM