Reputation:
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);
}
}
Upvotes: 0
Views: 228
Reputation: 24269
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
Upvotes: 1