Read from a file in C++ - c++

4;
Spadina;76 156
Bathurst;121 291
Keele;70 61
Bay;158 158
This is what file contains in it. I need to read them and save them into variables.
4 is for dynamic memory allocation. 4 means there are 4 stations.
Spadina, Bathrust, etc.. they are station names. first number, which comes right after station names, is number of student passes and the second number is number of adult pass.
So, basically I have 4 variables and they are;
int numberOfStation;
int studentPass;
int adultPass;
string stationName;
I spent 4 hours but still cannot read the file and save it into variable
Thank you.

A possible solution is to read every line with e.g. std::getline then parse each such line string. You'll use the appropriate methods of std::string to search inside it (with find) and split it (with substr). You might also access some individual character in that string using at or the [] operator of std::string; alternatively, you might perhaps parse each line -or relevant parts of them- using std::istringstream (I am not sure it is appropriate in your case). You might be interested by std::to_string...
An important thing is to define exactly (not only thru examples) the possible acceptable inputs. You could for example use some EBNF to formalize that. You should probably care about character encoding (try first by assuming a simple single-byte encoding like ASCII, then later consider UTF-8 if your system uses it).
For example, can the station names (I guess you talk about subway stations) contain digits, or spaces, or underscores, or commas, etc.... ? Could they be French names like Hôtel de Ville (a metro station in central Paris) or Saint-Paul or Bourg-La-Reine (where I am), or Russian names like Молодёжная in Moscow? (I guess that station names in Tokyo or in Jerusalem might be even funnier to parse).
BTW, explicitly entering the number of entries (like your initial 4) is very user-unfriendly. You could have some lexical conventions and e.g. use some tagging or separators.
At last, you might want to keep the information for every travel. Then you'll probably need to define some struct or class (not simply four scalar variables). At that point your program is becoming more interesting!

First group your variables in struct.
struct MyStruct
{
int studentPass;
int adultPass;
string stationName;
};
Now read size of struct in file and allocate it dynamically
MyStruct *p;
s >> N;
p = new MyStruct[N];
Now in for loop you read string with delimiter ';' and other two vars are ints
for (int i = 0; i < N; i++)
{
getline(s, p[i].stationName, ';');
s >> p[i].studentPass >> p[i].adultPass;
}
Where var s is istream type of variable with flag std::in

I recommend that you create a struct to hold your stations:
struct station{
string _stationName;
int _studentPass;
int _adultPass;
};
Then create an operator to use with your struct (props to Sly_TheKing for the getline idea):
std::istream& operator>>(std::istream& is, station& rhs){
getline(is, rhs._stationName, ';');
is >> rhs._studentPass >> rhs._adultPass >> ws;
return is;
}
Say that your ifstream is called foo. You can read these into a vector like this:
foo.ignore(numeric_limits<streamsize>::max(), '\n');
vector<station> bar{istream_iterator<station>(foo), istream_iterator<station>()};

Related

integers, chars and floating points in structs

So, I'm having some issues with my c++ code. I have the following code, but so far I can't get most of the data stored into the structured data type.
//structured data declaration
struct item
{
int itemCode;
char description[20];
float price;
};
And then the get code looks like this.
cout << setprecision(2) << fixed << showpoint;
ofstream salesFile ("Sales.txt");
ifstream stockFile ("Stock.txt");
for (counter = 0; counter < 9; counter++)
{
stockFile >> instock[counter].itemCode;
stockFile.getline (instock[counter].description, 20);
stockFile >> instock[counter].price;
}
The output should have looked like:
1234 "description here" 999.99
Quantity X
And this was the output:
1234 0.00
Quantity 5
If you have a file format that is of the form (for one entry)
1234
description here
999.99
(across multiple lines) then the explanation is simple
Th reading code in your loop, which does
stockFile >> instock[counter].itemCode;
stockFile.getline (instock[counter].description, 20);
stockFile >> instock[counter].price;
will work in this sequence
The value of instock[counter].itemCode will receive the value 1234. However (and this is important to understand) the newline after the 1234 will still be waiting in the stream to be read.
The call of getline() will encounter the newline, and return immediately. instock[counter].description will contain the string "".
The expression stockFile >> instock[counter].price will encounter the d in description. This cannot be interpreted as an integral value, so instock[counter].price will be unchanged.
Assuming some preceding code (which you haven't shown) sets instock[counter].price to 999.99 the above sequence of events will explain your output.
The real problem is that you are mixing styles of input on the one stream. In this case, mixing usage of streaming operators >> with use of line-oriented input (getline()). As per my description of the sequence above, different styles of input interact in different ways, because (as in this case) they behave differently when encountering a newline.
Some people will just tell you to skip over the newline after reading instock[counter].itemCode. That advice is flawed, since it doesn't cope well with changes (e.g. what happens if the file format changes to include an additional field on another line?, what happens if the file isn't "quite" in the expected format for some reason?).
The more general solution is to avoid mixing styles of input on the one stream. A common way would be to use getline() to read all data from the stream (i.e. not use >> to interact directly with stockFile). Then interpret/parse each string to find the information needed.
Incidentally, rather than using arrays of char to hold a string, try using the standard std::string (from standard header <string>). This has the advantage that std::string can adjust its length as needed. std::getline() also has an overload that can happily read to an std::string. Once data is read from your stream as an std::string, it can be interpreted as needed.
There are many ways of interpreting a string (e.g. to extract integral values from it). I'll leave finding an approach for that as an exercise - you will learn more by doing it yourself.

How to divide the input taken from user in two parts and assign them to two different arrays in C++?

If we take the input from user by asking them, like below:
cout << "Enter your course code and course name: ";
Now if the user enters CS201 Introduction to Programming, how can I only assign the code part, i.e. CS201 to an array, let's say;
char courseCode[10];
And how can I assign the name part in the array, let's say:
char courseName[50];
I want to do this to 5 students, using the structure defined below:
struct student
{
char courseName[50];
char courseCode[10];
};
student stu[5];
It's actually kind of simple once you remember that the input operator >> stops on white-space, and also know about the std::getline function.
Then you can do something like
std::string courseCode;
std::string courseName;
std::cin >> courseCode;
std::getline(std::cin, courseName);
Note that I use std::string for the strings instead of arrays. This is what you really should use. If you're not allowed (by your teacher or something) and must use arrays, then you can't use std::getline but instead have to use std::istream::getline instead.
Store the input in a single string say x
Now on x perform linear search for the first whitespace and split the string about the first whitespace. Store the two resultant strings in your struct.
I solved my problem using the cin.getline() functions to get the string in token pointer and then used strchr(char [], cahr) of <string> header file to separate the current string from the place where the first white space comes. Then I copied both separated strings into my desired elements of my structure using strcpy() function.

Reading comma separated data from string into struct, ignoring certain fields

About the data:
Fields are separated by comma ','
It is always a certain number of fields (in my case 33)
Fields are always in the same order
The data is of different types, represented in ASCII. The types are known to me
Some fields are always empty, and some are always to be ignored (not useful data)
Example of data:
AnIdentifier,1,G,2014-10-01,11:00,,,3,7555,1,14535,1 .. etc
I am wondering what is the best way to read this data into a struct, in a C++ way. I used sscanf earlier, but I thought the format-parameter of the call got rather big and messy.
I also tried splitting the string into tokens in a vector, before setting the values in the struct one by one with values from the vector, but then I didn't have the nice automatic data conversion.
I saw an example of overloading the >> operator in the struct:
struct MyDataStruct
{
std::string Id;
char D;
char S;
std::string Date; // Preferably these could be combined
std::string Time; // to a time_t?
bool Running;
int Runtime;
...
...
friend std::istream &operator>>(std::istream &is, MyDataStruct &ds) {
return is >> Id >> D >> S >> Date >> Time >> Running >> Runtime ...
}
}
My C++ knowledge is limited (used to C#), so I started to struggle when there were fields I wanted to ignore while reading into the struct. Say there are 5 fields in is after reading into Time that I wish to skip before reading into Running.
And say I wished to represent the timestamp by a time_t instead of the two strings Date and Time (the way they are represented in the received data), could this be accomplished directly in the operator overload while reading into the struct?

C++ Reading an multiline file with lines with arbitary lengths and format without using a stringstream

I have an input stream with the following lines:
# <int> <int>
<some_data_type> <some_data_type> <some_data_type> ..... <some_data_type>
<some_data_type_1> <some_data_type_2> <some_data_type_3> <some_data_type_1> <some_data_type_2> <some_data_type_3> .... <some_data_type_1> <some_data_type_2> <some_data_type_3>
In the above stream all three lines are different and have to be parsed differently. Currently,I am using a reading method as follows:
void reader( std::istream & is, DataStructure & d ){
std::string line;
getline(is,line);
std::stringstream s(line);
//parse line 1
getline(is,line);
std::stringstream line2(line);
//parse line 2
getline(is,line);
std::stringstream line3(line);
//parse line 3
}
Now the idea is not to make use of std::stringstream at all, as a line can arbitarily large and we donot want to load everything into memory twice. So, it would be better if it was possible to read from the input stream directly into the user given datastructure d.
An idea is to make use of std::istream_iterator but unfortunately the different lines have different parsing needs. For example, in the last line, three elements from the stream together constitute a single data element.
The only idea that seems plausible to me at this moment is to handle the stream buffer directly. It would be great if anyone could recommend a better way of doing this.
NOTE: Cannot make use of a tertiary data structure like std::stringstream. It is essential to read from the stream directly into the user provided data structure.
EDIT: Please note we are only allowed a single pass over the file.
Now the idea is not to make use of std::stringstream at all, as a line
can arbitarily large and we donot want to load everything into memory
twice. So, it would be better if it was possible to read from the
input stream directly into the user given datastructure d.
Olaf explained the extraction operator above but then we have a new requirement:
This will only work for the first line, where it is known there is a
fixed number of elements.
and
(2) Unfortunately, I have no discriminator beyond my knowledge that each instance of the data
structure needs to be instantiated with information stored in three
different lines. All three lines have different lengths and different
data elements. Also, I cannot change the format.
plus
(3) All information is treated as unsigned integer.
Now the next issue is that we don't know what the data structure actually is, so given what has come before it appears to be dynamic in some fashion. Because we can treat the data as unsigned int then we can use the extraction operator possibly, but read into a dynamic member:
vector<unsigned int> myUInts;
...
inFile >> currentUInt;
myUInts.push_back(currentUInt);
But then the issue of where to stop comes into play. Is it at the end of the first line, the third? If you need to read an arbitrary number of unsigned ints, whilst still checking for a new line then you will need to process white space as well:
inFile.unsetf(ios_base::skipws);
How you actually handle that is beyond what I can say at the moment without some clearer requirements. But I would guess it will be in the form:
inFile >> myMember;
char next = infile.peek()
//skip whitespace and check for new line
//Repeat until data structure filled, and repeat for each data structure.
Then do not use std::getline() at all. Define an istream operator for your types and use these directly
std::istream &operator >>(std::istream &f, DataStructure &d)
{
f >> d.member1 >> d.member2 >> ...;
return f;
}
void reader(std::istream & is, DataStructure &d)
{
is >> d;
}
There's no need fiddling with an std::istream_iterator or directly manipulating the stream buffer.

Reading constant number of characters in C++

How would you read a string if it was no longer than 20 characters and could contain spaces in C++? For example I have this kind of file:
3
Namama 4 5 12
Cool 4 2 34
Not So Cool 4 2 45
I want to write it in the most basic way possible. And I would prefer using only C++ (no C thingies) in this case. This means I want to store it in std::string.
I could use istream::get or istream::getline but then I have to avoid the new line characters. This is problematic (and the code I'm writing is going to be shown to beginners).
What are your solutions for this problem?
Edit:
As asked I'm going to tell you what I have tried but I don't think it's going to be any good. I am not a C++ beginner and usually I would have used something like istream::get and char array but removing new line characters might seem too techy for some people.
So... Either reading char array or std::string using istream::operator>> fails because it stops reading when they see space character (and I may have to read several words). This means the following code fails:
char name[21];
std::cin >> name;
or...
std::string name;
std::cin >> name;
Another thing is that new line characters differ from system to system and actually Windows use two of them by default and I have tried using istream::ignore with 2 as an argument on Windows and that was the only way to ignore the new line and I came to conclusion that it's because Windows use two characters for a new line mark. That means it wouldn't work on Linux and it would have to be more complex... Again - bad for beginners.
If you want to read exactly 20 characters,
for(int i=0; i<3; ++i) {
std::string result;
int one, two, three;
result.resize(20);
if (!std::cin.read(&result[0], 20)) throw std::runtime_error("reading buffer failed!");
if (!std::cin >> one >> two >> three) throw std::runtime_error("reading numbers failed!");
if (!std::cin.ignore(1000, '\n')) throw std::runtime_error("ignore failed!");
}
If you don't want exactly 20 characters, how do you know when you've reached the end of the string?
One way to accomplish this would be to use istream::read into a temporary buffer of sufficient size, and then use that buffer to initialize a std::string.
char name[21];
std::cin.read(name, 20);
std::string nameString(name); // If you want it in a std::string
This reads exactly 20 characters, newlines and everything. If it's for an exercise, then this is at the very least very simple. If you sometimes want less than 20 characters, it is possible to determine how many extra were read and back the get pointer by std::istream::unget or equivalent.