Reading variables from a configuration file - c++

I'm trying to determine the best way to read in a configuration file. This "Parameters.cfg" file is just to define values and is of this form:
origin_uniform_distribution 0
origin_defined 1
angles_gaussian 0
angles_uniform_distribution 0
angles_defined 0
startx 0
starty 0
gap 500
nevents 1000
origin_uniform_distribution_x_min -5
origin_uniform_distribution_x_max 5
origin_uniform_distribution_y_min -5
origin_uniform_distribution_y_max 5
origin_defined_x 0
origin_defined_y 0
angles_gaussian_center 0
angles_gaussian_sigma 5
angles_uniform_distribution_x_min -5
angles_uniform_distribution_x_max 5
angles_uniform_distribution_y_min -5
angles_uniform_distribution_y_max 5
angles_defined_x 10
angles_defined_y 10
The names are there for the user to know which variables they are defining. I would like to have my program read in only the actual numbers and skip over the strings. I know I can do this in a way where I define a whole lot of strings in my program, and then just leave them sitting there defined but obviously unused. Is there a way to read in the numbers easily while skipping over the strings?

What's wrong with the obvious solution?
string param_name;
int param_value;
while ( fin >> param_name >> param_value )
{
..
}
You can discard the param_name after each iteration while storing the param_value wherever you need it.

When you read out the strings, just don't store them anywhere:
std::vector<int> values;
std::string discard;
int value;
while (file >> discard >> value) {
values.push_back(value);
}

I guess I must be overdue to post a ctype facet to ignore the strings and read only the data we care about:
#include <locale>
#include <fstream>
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
struct number_only: std::ctype<char> {
number_only() : std::ctype<char>(get_table()) {}
static mask const *get_table() {
static std::vector<mask> rc(table_size, space);
std::fill_n(&rc['0'], 10, digit);
rc['-'] = punct;
return &rc[0];
}
};
int main() {
// open the file
std::ifstream x("config.txt");
// have the file use our ctype facet:
x.imbue(std::locale(std::locale(), new number_only));
// initialize vector from the numbers in the file:
std::vector<int> numbers((std::istream_iterator<int>(x)),
std::istream_iterator<int>());
// display what we read:
std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
This way the extraneous data is really and truly ignored -- after imbuing the stream with our facet, it's as if the strings don't exist at all.

This method doesn't store the string at all (like was asked for in the question):
static const std::streamsize max = std::numeric_limits<std::streamsize>::max();
std::vector<int> values;
int value;
while(file.ignore(max, ' ') >> file >> value)
{
values.push_back(value);
}
It uses ignore instead of reading the string and not using it.

You can define a structure and then overload the istream operator>> for it:
struct ParameterDiscardingName {
int value;
operator int() const {
return value;
}
};
istream& operator>>(istream& is, ParameterDiscardingName& param) {
std::string discard;
return is >> discard >> param.value;
}
ifstream file("Parameters.cfg");
istream_iterator<ParameterDiscardingName> begin(file), end;
vector<int> parameters(begin, end);

Related

C++ Reading data from text file into array of structures

I am reasonably new to programming in C++ and i'm having some trouble reading data from a text file into an array of structures. I have looked around similar posts to try and find a solution however, I have been unable to make any of it work for me and wanted to ask for some help. Below is an example of my data set (P.S. I will be using multiple data sets of varying sizes):
00010 0
00011 1
00100 0
00101 1
00110 1
00111 0
01000 0
01001 1
Below is my code:
int variables = 5;
typedef struct {
int variables[variables];
int classification;
} myData;
//Get the number of rows in the file
int readData(string dataset)
{
int numLines = 0;
string line;
ifstream dataFile(dataset);
while (getline(dataFile, line))
{
++numLines;
}
return numLines;
}
//Store data set into array of data structure
int storeData(string dataset)
{
int numLines = readData(dataset);
myData *dataArray = new myData[numLines];
...
return 0;
}
int main()
{
storeData("dataset.txt");
What I am trying to achieve is to store the first 5 integers of each row of the text file into the 'variables' array in the 'myData' structure and then store the last integer separated by white space into the 'classification' variable and then store that structure into the array 'dataArray' and then move onto the next row.
For example, the first structure in the array will have the variables [00010] and the classification will be 0. The second will have the variables [00011] and the classification will be 1, and so on.
I would really appreciate some help with this, cheers!
Provide stream extraction and stream insertion operators for your type:
#include <cstddef> // std::size_t
#include <cstdlib> // EXIT_FAILURE
#include <cctype> // std::isspace(), std::isdigit()
#include <vector> // std::vector<>
#include <iterator> // std::istream_iterator<>, std::ostream_iterator<>
#include <fstream> // std::ifstream
#include <iostream> // std::cout, std::cerr, std::cin
#include <algorithm> // std::copy()
constexpr std::size_t size{ 5 };
struct Data {
int variables[size];
int classification;
};
// stream extraction operator
std::istream& operator>>(std::istream &is, Data &data)
{
Data temp; // don't write directly to data since extraction might fail
// at any point which would leave data in an undefined state.
int ch; // signed integer because std::istream::peek() and ...get() return
// EOF when they encounter the end of the file which is usually -1.
// don't feed std::isspace
// signed values
while ((ch = is.peek()) != EOF && std::isspace(static_cast<unsigned>(ch)))
is.get(); // read and discard whitespace
// as long as
// +- we didn't read all variables
// | +-- the input stream is in good state
// | | +-- and the character read is not EOF
// | | |
for (std::size_t i{}; i < size && is && (ch = is.get()) != EOF; ++i)
if (std::isdigit(static_cast<unsigned>(ch)))
temp.variables[i] = ch - '0'; // if it is a digit, assign it to our temp
else is.setstate(std::ios_base::failbit); // else set the stream to a
// failed state which will
// cause the loop to end (is)
if (!(is >> temp.classification)) // if extraction of the integer following the
return is; // variables fails, exit.
data = temp; // everything fine, assign temp to data
return is;
}
// stream insertion operator
std::ostream& operator<<(std::ostream &os, Data const &data)
{
std::copy(std::begin(data.variables), std::end(data.variables),
std::ostream_iterator<int>{ os });
os << ' ' << data.classification;
return os;
}
int main()
{
char const *filename{ "test.txt" };
std::ifstream is{ filename };
if (!is.is_open()) {
std::cerr << "Failed to open \"" << filename << "\" for reading :(\n\n";
return EXIT_FAILURE;
}
// read from ifstream
std::vector<Data> my_data{ std::istream_iterator<Data>{ is },
std::istream_iterator<Data>{} };
// print to ostream
std::copy(my_data.begin(), my_data.end(),
std::ostream_iterator<Data>{ std::cout, "\n" });
}
Uncommented it looks less scary:
std::istream& operator>>(std::istream &is, Data &data)
{
Data temp;
int ch;
while ((ch = is.peek()) != EOF && std::isspace(static_cast<unsigned>(ch)))
is.get();
for (std::size_t i{}; i < size && is && (ch = is.get()) != EOF; ++i)
if (std::isdigit(static_cast<unsigned>(ch)))
temp.variables[i] = ch - '0';
else is.setstate(std::ios_base::failbit);
if (!(is >> temp.classification))
return is;
data = temp;
return is;
}
std::ostream& operator<<(std::ostream &os, Data const &data)
{
std::copy(std::begin(data.variables), std::end(data.variables),
std::ostream_iterator<int>{ os });
os << ' ' << data.classification;
return os;
}
It looks line you are trying to keep binary values as integer index. If that is the case, it will be converted into integer internally. You may need int to binary conversion again.
If you want to preserve data as is in the text file, then you need to choose either char/string type for the index value. For classification, it seems value will be either 0 or 1. So you can choose bool as data type.
#include <iostream>
#include <map>
using namespace std;
std::map<string, bool> myData;
int main()
{
// THIS IS SAMPLE INSERT. INTRODUCE LOOP FOR INSERT.
/*00010 0
00011 1
00100 0
00101 1
00110 1*/
myData.insert(std::pair<string, bool>("00010", 0));
myData.insert(std::pair<string, bool>("00011", 1));
myData.insert(std::pair<string, bool>("00100", 0));
myData.insert(std::pair<string, bool>("00101", 1));
myData.insert(std::pair<string, bool>("00110", 1));
// Display contents
std::cout << "My Data:\n";
std::map<string, bool>::iterator it;
for (it=myData.begin(); it!=myData.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';
return 0;
}

Reading a input file into a vector

I'm trying to read a file of int's and double's into a vector but I am having difficulty doing so. Given something like:
1 2.1 3 4
2 4
3
9 0.1
How can I use ifstream and the getline function to convert the string into integers and doubles & inserting this into a vector?
I know this is incorrect but I am thinking of something along the lines of:
vector<Pair *> vec; //Pair is a class that contains a int & a double data member
string str;
double num;
ifstream f;
f.open("name of file");
while(getline(f, str){
num = stod(str);
}
To insert into the vector I believe I can do something along the lines of:
Pair * pairObj = new Pair(x,y); //"x" being of type int and "y" being of type double
v.push_back(pair);
I'm sorry if this is unclear, please let me know and I will do my best to explain myself.
You should just use stream iterators!
#include <iostream> // for IO
#include <vector> // for vector!
#include <iterator> // for stream iterator
#include <algorithm> // for copy (optional)
if you are directly initializing
vector<double>vdata{istream_iterator<double>(ifile),
istream_iterator<double>()};
else use copy or copy_n if you only want a fixed amount of data
copy(istream_iterator<double>(ifile),
istream_iterator<double(),
back_inserter(vdata));
if you are working with a large file i would recommend using this method
vector<doube>vdata;
// this will save alot of time, if you don't resize the vector must keep reallocating data
vdata.reserve(file_size);
copy(istream_iterator<double>(ifile),
istream_iterator<double>(),
back_inserter(vdata));
strtod() is C. Proper C++ uses the >> operator.
Once you have read each line of text, construct a std::istringstream from the string, then use operator>> to parse it.
Something along these line::
std::ifstream f("name of file");
// Check if the file was succesfully opened, etc...
std::string str;
while( getline(f, str))
{
std::istringstream i(str);
std::vector<double> v;
double d;
while (i >> d)
{
v.push_back(d);
}
if (!i.eof())
{
// Must be a parsing failure, deal with it in some way.
}
else
{
// Otherwise, v is the vector of numbers on this line.
}
}
string str;
std::vector< double> vd;
// loop reading lines of input
while( getline( f, str )
{
std::stringstream sst(str);
std::string a;
// loop reading space separated values in line
while( getline( sst, a, ' ' ) )
// conver to double and add to end of vectior
vd.push_back( stod( a );
}
// check for complete pairs
if( vd.size() % 2 )
cout << "Error!"
// loop over pairs
vector< pair<int,double> > vpairs;
for( int kp = 0; kp < vd.size()/2; kp++ )
vpairs.push_back( pair<int,double>( (int)vd[kp*2],vd[kp*2+1) );

Reading an Input File And Store The Data Into an Array (beginner)!

The Input file:
1 4 red
2 0 blue
3 1 white
4 2 green
5 2 black
what I want to do is take every row and store it into 2D array.
for example:
array[0][0] = 1
array[0][1] = 4
array[0][2] = red
array[1][0] = 2
array[1][1] = 0
array[1][2] = blue
etc..
code Iam working on it:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int convert_str_to_int(const string& str) {
int val;
stringstream ss;
ss << str;
ss >> val;
return val;
}
string getid(string str){
istringstream iss(str);
string pid;
iss >> pid;
return pid;
}
string getnumberofcolors(string str){
istringstream iss(str);
string pid,c;
iss >> pid>>c;
return c;
}
int main() {
string lineinfile ;
vector<string> lines;
ifstream infile("myinputfile.txt");
if ( infile ) {
while ( getline( infile , lineinfile ) ) {
lines.push_back(lineinfile);
}
}
//first line - number of items
int numofitems = convert_str_to_int(lines[0]);
//lopps items info
string ar[numofitems ][3];
int i = 1;
while(i<=numofitems ){
ar[i][0] = getid(lines[i]);
i++;
}
while(i<=numofitems ){
ar[i][1] = getarrivel(lines[i]);
i++;
}
infile.close( ) ;
return 0 ;
}
when I add the second while loop my program stopped working for some reason!
is there any other way to to this or a solution to my program to fix it.
It's better to show you how to do it much better:
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main() {
ifstream infile("myinputfile.txt"); // Streams skip spaces and line breaks
//first line - number of items
size_t numofitems;
infile >> numofitems;
//lopps items info
vector<pair<int, pair<int, string>> ar(numofitems); // Or use std::tuple
for(size_t i = 0; i < numofitems; ++i){
infile >> ar[i].first >> ar[i].second.first >> ar[i].second.second;
}
// infile.close( ) ; // Not needed -- closed automatically
return 0 ;
}
You are probably solving some kind of simple algorithmic task. Take a look at std::pair and std::tuple, which are useful not only as container for two elements, but because of their natural comparison operators.
The answer given is indeed a much better solution than your's. I figured i should point out some of your design flaws and give some tips too improve it.
You redefined a function that already exists in the standard, which is
std::stoi() to convert a string to an integer. Remember, if a function
exists already, it's OK to reuse it, don't think you have to reinvent what's
already been invented. If you're not sure search your favorite c++ reference guide.
The solution stores the data "as is" while you store it as a full string. This doesn't really make sense. You know what the data is beforehand, use that to your advantage. Plus, when you store a line of data like that it must be parsed, converted, and then constructed before it can be used in any way, whereas in the solution the data is constructed once and only once.
Because the format of the data is known beforehand an even better way to load the information is by defining a structure, along with input/output operators. This would look something like this:
struct MyData
{
int num1;
int num2;
std::string color;
friend std::ostream& operator << (std::ostream& os, const MyData& d);
friend std::istream& operator >> (std::istream& os, const MyData& d);
};
Then you could simply do something like this:
...
MyData tmp;
outfile << tmp;
vData.push_back(tmp);
...
Their is no question of intent, we are obviously reading a data type from a stream and storing it in a container. If anything, it's clearer as to what you are doing than either your original solution or the provided one.

Reading numbers from file to string to array

I am reading numbers from a file, say:
1 2 3 4 5
I want to read this data from a file into a string into an array for further processing. Here's what I've done:
float *ar = nullptr;
while (getline(inFile, line))
{
ar = new float[line.length()];
for (unsigned int i = 0; i < line.length(); i++)
{
stringstream ss(line);
ss >> ar[i];
}
}
unsigned int arsize = sizeof(ar) / sizeof(ar[0]);
delete ar;
Suffice it to say that it works insofar it only gets the first value from the file. How do I get the array to be input ALL the values? I debugged the program and I can confirm that line has all the necessary values; but the float array doesn't. Please help, thanks!
line.length() is the number of characters in the line, not the number of words/numbers/whatevers.
Use a vector, which can be easily resized, rather than trying to juggle pointers.
std::vector<float> ar;
std::stringstream ss(line);
float value;
while (ss >> value) { // or (inFile >> value) if you don't care about lines
ar.push_back(value);
}
The size is now available as ar.size(); your use of sizeof wouldn't work since ar is a pointer, not an array.
The easiest option is to use the standard library and its streams.
$ cat test.data
1.2 2.4 3 4 5
Given the file you can use the stream library like this:
#include <fstream>
#include <vector>
#include <iostream>
int main(int argc, char *argv[]) {
std::ifstream file("./test.data", std::ios::in);
std::vector<float> res(std::istream_iterator<float>(file),
(std::istream_iterator<float>()));
// and print it to the standard out
std::copy(std::begin(res), std::end(res),
std::ostream_iterator<float>(std::cout, "\n"));
return 0;
}
I ran into this problem earlier when I wanted to extract data line by line from a file to fill my sql database that I wanted to use.
There are many solutions to this specific problem such as:
The solution is using stringstream with a while statement to put data from file into the array with a while statement
//EDIT
While statement with getline
//This solution isn't very complex and is pretty easy to use.
New Improved simple solution:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main()
{
ifstream line;
line.open("__FILENAME__");
string s;
vector<string> lines;
while(getline(line, s))
{
lines.push_back(s);
}
for(int i = 0;i < lines.size();i++)
{
cout << lines[i] << " ";
}
return 0;
}
compiled code to check - http://ideone.com/kBX45a
What about atof?
std::string value = "1.5";
auto converted = atof ( value.c_str() );
Rather complete:
while ( std::getline ( string ) )
{
std::vector < std::string > splitted;
boost::split ( splitted, line, boost::is_any_of ( " " ) );
std::vector < double > values;
for ( auto const& str: splitted ) {
auto value = atof ( str.c_str() );
values.push_back ( value );
}
}

C++ formatted input must match value

I'm reading a file with C++; the file looks like:
tag1 2345
tag2 3425
tag3 3457
I would like to have something like
input>>must_be("tag1")>>var1>>must_be("tag2")>>var2>>must_be("tag3")>>var3;
Where everything blows up if what's being taken in doesn't match the argument of must_be() and, when done, var1=2345, var2=3425, var3=3457.
Is there a standard way of doing this? (Hopefully where "tag1" need not necessarily be a string, but this is not a requirement.) fscanf from C made it quite easy.
Thanks!
To clarify, each >> reads in one whitespace-delimited set of characters from input. I want to match some of the in-coming blocks of characters (tagX) against strings or data I have specified.
You need to implement operator>> for your class. Something like this :
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
struct A
{
A(const int tag_):tag(tag_),v(0){}
int tag;
int v;
};
#define ASSERT_CHECK( chk, err ) \
if ( !( chk ) ) \
throw std::string(err);
std::istream& operator>>( std::istream & is, A &a )
{
std::string tag;
is >> tag;
ASSERT_CHECK( tag.size() == 4, "tag size" );
std::stringstream ss(std::string(tag.begin()+3,tag.end()));
int tagVal;
ss >> tagVal;
std::cout<<"tag="<<tagVal<<" a.tag="<<a.tag<<std::endl;
ASSERT_CHECK( a.tag == tagVal,"tag value" );
is >> a.v;
return is;
}
int main() {
A a1(1);
A a2(2);
A a3(4);
try{
std::fstream f("in.txt" );
f >> a1 >> a2 >> a3;
}
catch(const std::string &e)
{
std::cout<<e<<std::endl;
}
std::cout<<"a1.v="<<a1.v<<std::endl;
std::cout<<"a2.v="<<a2.v<<std::endl;
std::cout<<"a3.v="<<a3.v<<std::endl;
}
Take a note that for wrong tag value, an exception will be thrown (meaning the tag much match).
Can't you read it line by line, and matching tags for each line? If the tag doesn't match what you expect you just skip the line and move on to the next.
Something like this:
const char *tags[] = {
"tag1",
"tag2",
"tag3",
};
int current_tag = 0; // tag1
const int tag_count = 3; // number of entries in the tags array
std::map<std::string, int> values;
std::string line;
while (current_tag < tag_count && std::getline(input, line))
{
std::istringstream is(line);
std::string tag;
int value;
is >> tag >> value;
if (tag == tags[current_tag])
values[tag] = value;
// else skip line (print error message perhaps?)
current_tag++;
}