Load variables from file and work with them

I read some threads about importing and displaying data from files, but didnt find a case of actually using imported data as function arguments. Especially not with a case of mixed variables.

As i'd like to add that functionality to my learning program, i have a these questions:

//Data format is "int int int double double" per line, with multiple lines, but i'd start with just a single line and then i guess i'll use loop that looks for end of line or file

- is std::vector the best choice for it ? (as per advice on learncpp)
- unions are the only way to handle mixed types ?
- sub: two vectors/arrays/structs ?
- did i miss something ?

Thinking about it make me wonder if a class wouldnt be best as a "container", as there are several functions that need thoes variables.
Last edited on
is std::vector the best choice for it ? (as per advice on learncpp)

That depends on what you want to do with the data. You haven't specified what you want to do with the data once you've read it. If you don't need to accumulate the data while reading the file, there is no need for a vector.

unions are the only way to handle mixed types ?

What do you mean by "mixed types"? Is your file not a consistent format?

sub: two vectors/arrays/structs ?

??? - What are you trying to say?

it make me wonder if a class wouldn't be best as a "container"

Or a struct. I'd use a class if the data in a row represents some "object" that has member functions. I'd use a struct if it's just Plain Old Data (POD). This only difference between a class and a struct is the default visibility of members.

Last edited on
What i want it is to use the imported numbers in some calculations:

1
2
3
4
5
6
7
8
9
10
11
12
13
long double lattConstant(int h, int k, int l, long double d)
{
    long double a;
    a = sqrt((pow(h,2) + pow(k,2) + pow(l,2))*(pow(d,2)));
    return a;
}

long double lattSpacCubic(int h, int k, int l, long double a)
{
    long double d;
    d = a/sqrt(pow(h,2)+pow(k,2)+pow(l,2));
    return d;
}


and save the results to a file:

1
2
3
4
5
6
7
8
9
10
11
void calcLatticeParamManual()
{
    int h = millerIndex('h');
    int k = millerIndex('k');
    int l = millerIndex('l');
    long double d = lattSpacing();
    long double a = lattConstant(h, k, l, d);
    std::cout << "(" << h << k << l << ")" << "\t" << a << "\t" << d << std::endl; //shows result
    std::ofstream o( "Lattice Parameters.txt", std::ios::app ); //create txt file with name and append
    o << "hkl(" << h << k << l << ")\t a=" << a <<"A\t d_hkl " << d << "A" << std::endl;
}


That's the manual way (with some of input functions skipped). With a file, it'd be all automatic (at least that's the idea).

By mixed types i understand (int+double+char) for instance. I guess a struct can hold them, but i read that a vector needs something called a union and that i do not know yet.

That's sub: blah blah was broken, sorry. What i meant was should i maybe try two separate vectors (one for int, other for double).
Cant figure it on my own.

If i didnt describe correctly my problem, i'll try again:
1. import data from file (format: int int int double double)
2. data goes into a table (struct, vector, something else)

Said variables will be used in some combinations in calculations.
I would do it like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <vector>

using namespace std;

struct Data
{
  int h,
      k,
      l;
  double d,
         a;
};


int main ()
{
  vector<Data> data;
  ifstream src ("YourFilename");

  if (!src)
  {
    perror ("File error: ");
    system ("pause");
    exit (EXIT_FAILURE);
  }
  Data tmp;
  while (src >> tmp.h >> tmp.k >> tmp.l >> tmp.a >> tmp.d)
  {
    data.push_back (tmp);
  }

  // process your vector here

  system ("pause"); // remove if you don't use Visual Studio
  return 0;
}

Thank you. I will read through your example and try to understand how it works.
Last edited on
Hmmmm...
I have two equations using these parameters. Even better, second equation is using the result of the first and both values are needed. (i also noticed i pasted the wrong function above making you use 'a' and 'd' instead of 'wl' and 't')
First is:
1
2
3
4
5
6
7
8
long double lattSpacing()
{
    long double wl = defineWaveLength();
    long double t = doubleTheta();
    long double d;
    d =(wl/(2*sin(t)));
    return d;
}


Second is:
1
2
3
4
5
6
long double lattConstant(int h, int k, int l, long double d)
{
    long double a;
    a = sqrt((pow(h,2) + pow(k,2) + pow(l,2))*(pow(d,2)));
    return a;
}


The above functions should be called by another function, to save the results to a file.
Here's the manual version:
1
2
3
4
5
6
7
8
9
10
11
void calcLatticeParamManual()
{
    int h = millerIndex('h');
    int k = millerIndex('k');
    int l = millerIndex('l');
    long double d = lattSpacing();
    long double a = lattConstant(h, k, l, d);
    std::cout << "d_" << h << k << l << "=" << d << "[nm]\ta=" << a << "[nm]\t" << std::endl;
    std::ofstream o( "Lattice Parameters.txt", std::ios::app );
    o << "d_" << h << k << l << "=" << d << "[nm]\ta=" << a << "[nm]\t" << std::endl;
}


So i tried doing a version of the above for an automatic file input.
I made your function into a separate ().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void getData()
{
  vector<Data> data;
  ifstream src ("YourFilename");

  if (!src)
  {
    perror ("File error: ");
    system ("pause");
    exit (EXIT_FAILURE);
  }
  Data tmp;
  while (src >> tmp.h >> tmp.k >> tmp.l >> tmp.a >> tmp.d)
  {
    data.push_back (tmp);
  }


And placed it inside:

1
2
3
4
5
6
7
8
9
void calcLatticeParamAuto()
{
    getData(); //this is the function you provided, Thomas1965
    long double d = lattSpacing(); //this () and below call functions that calculate values that i want to know
    long double a = lattConstant(Data.tmp.h, Data.tmp.k, Data.tmp.l, d);
    std::cout << "(" << Data.tmp.h << Data.tmp.k << Data.tmp.l << ")" << "\t" << a << "\t" << d << std::endl; //shows result
    std::ofstream o( "Lattice Parameters.txt", std::ios::app ); //create txt file with name and append
    o << "hkl(" << Data.tmp.h << Data.tmp.k << Data.tmp.l << ")\t a=" << a <<"A\t d_hkl " << d << "A" << std::endl; //this saves to a file
}


Doesnt want to work.

(...)crystal_functions.cpp|220|error: expected primary-expression before '.' token|

Line 220 corresponds with long double a = lattConstant(Data.tmp.h, Data.tmp.k, Data.tmp.l, d);(line 5 in last code block).
As a side note, the struct is defined separately and everything is in the header.
Last edited on
Literally just blasted through this let me know if it works (or does not!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <fstream>
#include <vector>

struct Data
{
	int a;
	int b;
	int c;
	double d;
	double e;
}

// Could make this friend instead
std::istream& operator>>(std::istream& is, Data data)
{
	is >> data.a >> data.b >> data.c >> data.d >> data.e;
	return is;
}

std::ostream& operator<<(std::ostream& os, Data d)
{
	os << data.a << data.b << data.c << data.d << data.e;
	return os;
}

std::vector<Data> LoadData(std::string file)
{
	std::ifstream ifs(file.c_str());
	std::vector<Data> result;
	Data d;
	while(ifs)
	{
		ifs >> d;
		result.push_back(d);
	}
	return result;
}

bool SaveData(std::vector<Data>& data, std::string filename)
{
	if(data.size() < 1) return 0; // No data
	std::ifstream ifs(filename.c_str());
	if(ifs) return 0; // Filename already exists
	ifs.close();
	std::ofstream ofs(filename.c_str());
	for(auto i : data) ofs << i;
	return 1; // Success
}

int main()
{
	// Usage
	std::vector<Data> file_data = LoadData("filename.dat");
	SaveData(file_data,"New_file.dat");
}
Changed variable names to match mine (like 'd' to 'wl')
Is 'd' in parameters the same as in the struct (meaning i should change it to 'wl') ?

1
2
3
4
5
std::ostream& operator<<(std::ostream& os, Data d)
{
	os << data.h << data.k << data.l << data.wl << data.t;
	return os;
}


generates


(...)error: 'data' was not declared in this scope
(...)warning: unused parameter 'd'
Last edited on
That was me rushing an answer for you, change data.h << ... to d.h << d.k ...

EDIT: Or change (std::ostream& os, Data d) to (std::ostream& os, Data data)

EDIT2:

To save confusion completely overwrite the function with this:

1
2
3
4
5
std::ostream& operator<<(std::ostream& os, Data data)
{
	os << data.a << data.b << data.c << data.d << data.e;
	return os;
}


But of course change the variable members of struct to whatever you chose them to (I.E h,k,wl etc)

EDIT3:

If that worked for you, another neat little trick you can do is make the Load and Save functions generic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template<typename T>
std::vector<T> LoadData(std::string file)
{
	std::ifstream ifs(file.c_str());
	std::vector<T> result;
	T d;
	while(ifs)
	{
		ifs >> d;
		result.push_back(d);
	}
	return result;
}

template<typename T>
bool SaveData(std::vector<T>& data, std::string filename)
{
	if(data.size() < 1) return 0; // No data
	std::ifstream ifs(filename.c_str());
	if(ifs) return 0; // Filename already exists
	ifs.close();
	std::ofstream ofs(filename.c_str());
	for(auto i : data) ofs << i;
	return 1; // Success
}


So long as you overload the fstream operators << and >> you can reuse it with any struct or class of variables.

Usage with the above code:

1
2
3
std::vector<Data> loaded = LoadData<Data>("Filename.dat");
std::vector<SomeOtherData> other_loaded = LoadData<SomeOtherData>("Other.dat");
SaveData<Data>(loaded,"NewSave.dat");
Last edited on
All of it compiled. Now i need to figure out what does what and how to properly use it to import and calculate.
Thank you for this.
BTW. Where can i find a "c++ step by step guide to fill a struct from a file". Because i kinda sorta would like to understand what i'm doing here :P
Hehe don't we all so much I don't understand :)

The functions I gave you is a good enough function to be able to load and save, here is what's happening:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Function returns a vector of Data objects
std::vector<Data> LoadData(std::string file)
{
	std::ifstream ifs(file.c_str()); // Open file with the filename given from the parameters
	// May want to check for errors, did it actually open?
        std::vector<Data> result; // Create an empty vector ready to fill with Data objects
	Data d;
	while(ifs) // While data can still be read
	{
		ifs >> d; // Thanks to overloading the fstream operators (std::istream& operator>>) we can skip the chaining (a >> b >> c >> d etc)
		result.push_back(d); // Now the Data object is filled add it to the vector
	}
	return result; // Return the vector of Data objects ready for processing
}

bool SaveData(std::vector<Data>& data, std::string filename)
{
	if(data.size() < 1) return 0; // No data
	std::ifstream ifs(filename.c_str());
	if(ifs) return 0; // Filename already exists
	ifs.close();
	std::ofstream ofs(filename.c_str());
       // I can't give you the exact name for this it does have one, but basically "i" will equal each Data object in the vector as it loops through
	for(auto i : data) ofs << i; // Again thanks to operating overloading no need to chain the output
	return 1; // Success
}


I did do a post years back showing how to load and save data from files, it's a very common question. :]

EDIT: To use this, whenever you want to load a series of Data objects, just call LoadData("FileNameHere.txt");

Whenever you want to save a vector of data objects, call SaveData(DataVectorHere,"FileNameHere.txt");
Last edited on
Thank you. I will read into it. Especially that i need to know and feel the way around filling from file and moving that data around before i can head towards classes and what-not.
before i can head towards classes and what-not


I was just like you, I wouldn't recommend doing that. Don't build a house with a stick, if a hammer is laying around in the same toolbox. :)

Classes and Structs are a great way to group data and access it all from one location:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Item
{
     Item(std::string n, double v): name(n), value(v) {}
     std::string name;
     double value;
};

std::ostream& operator<<(std::ostream& os, Item i)
{
    os << "Name: " << i.name << "\nValue: " << i.value;
    return os; // EDIT: OOPS! Don't forget to return the stream
}

int main()
{
    Item apple("Red Apple",0.35);
    std::cout << apple; // std::cout and std::ofstream are both derived from std::ostream
    return 0;
}


If you need any more help just PM me or I'm sure many others can help with your problems
Last edited on
I see. I'll think about it. Thank you again.
Topic archived. No new replies allowed.