Reading in from a txt file. Trouble parsing info - c++

I want to read in scores from a txt file. The scores are going into a struct.
struct playerScore
{
char name[32];
int score, difficulty;
float time;
};
the text file looks like this
Seth 26.255 40 7
as one line, where each item is followed by a tab. (Name\t time\t score\t difficulty\n)
When I begin to read in the text, I don't know how to tell the program when to stop. The scores file could be any number of lines or score entries. This is what I have attempted.
hs.open("scores.txt", ios_base::in);
hs.seekg(0, hs.beg);
if (hs.is_open())
{
int currpos = 0;
while (int(hs.tellg()) != int(hs.end));
{
hs>> inScore.name;
hs >> inScore.time;
hs >> inScore.score;
hs >> inScore.difficulty;
hs.ignore(INT_MAX, '\n');
AllScores.push_back(inScore);
currpos = (int)hs.tellg();
}
}
I'm trying to make a loop that will read in a line of code into a temp struct for the data, then push that struct into a vector of structs. Then update the currpos variable with the current location of the input pointer. However, the loop just gets stuck on the condition and freezes.

There are a multitude of ways to do this, but the following is likely what you're looking for. Declare a free-operator for extracting a single-line definition of a player-score:
std::istream& operator >>(std::istream& inf, playerScore& ps)
{
// read a single line.
std::string line;
if (std::getline(inf, line))
{
// use a string stream to parse line by line.
std::istringstream iss(line);
if (!(iss.getline(ps.name, sizeof(ps.name)/sizeof(*ps.name), '\t') &&
(iss >> ps.time >> ps.score >> ps.difficulty)))
{
// fails to parse a full record. set the top-stream fail-bit.
inf.setstate(std::ios::failbit);
}
}
return inf;
}
With that, your read code can now do this:
std::istream_iterator<playerScore> hs_it(hs), hs_eof;
std::vector<playerScore> scores(hs_it, hs_eof);

I dont think that you can just >> from your file. Do you think it will take everything till \t? :)
You can try to take for example token with strtok()
I guess it can use '\t' to split string and take for each variable via this function needed part of string
In case if it strtok() doesnt work that way i guess you can just copy till '\t' in sub-loop

You can do like this
playerScore s1;
fstream file;
file.open("scores.txt", ios::in | ios::out);
while(!file.eof()) //For end of while loop
{
file.read(s1, sizeof(playerScore));//read data in one structure.
AllScores.push_back(s1);
}

Related

Reading text with blanks and numeric data from a file

So I have data in a text like this:
Alaska 200 500
New Jersey 400 300
.
.
And I am using ifstream to open it.
This is part of a course assignment. We are not allowed to read in the whole line all at once and parse it into the various pieces. So trying to figure out how to read each part of every line.
Using >> will only read in "New" for "New Jersey" due to the white space/blank in the middle of that state name. Have tried a number of different things like .get(), .read(), .getline(). I have not been able to get the whole state name read in, and then read in the remainder of the numeric data for a given line.
I am wondering whether it is possible to read the whole line directly into a structure. Of course, structure is a new thing we are learning...
Any suggestions?
Can't you just read the state name in a loop?
Read a string from cin: if the first character of the string is numeric then you've reached the next field and you can exit the loop. Otherwise just append it to the state name and loop again.
Here is a line by line parsing solution that doesn't use any c-style parsing methods:
std::string line;
while (getline(ss, line) && !line.empty()) {
size_t startOfNumbers = line.find_first_of("0123456789");
size_t endOfName = line.find_last_not_of(" ", startOfNumbers);
std::string name = line.substr(0, endOfName); // Extract name
std::stringstream nums(line.substr(startOfNumbers)); // Get rest of the line
int num1, num2;
nums >> num1 >> num2; // Read numbers
std::cout << name << " " << num1 << " " << num2 << std::endl;
}
If you can't use getline, do it yourself: Read and store in a buffer until you find '\n'. In this case you probably also cannot use all the groovy stuff in std::string and algorithm and might as well use good ol' C programming at that point.
Once you have grabbed a line, read your way backwards from the end of the line and
Discard all whitespace until you find non whitespace.
Gather characters found into token 3 until you find whitepace again.
Read and discard the whitespace until you find the end of token 2.
Gather token 2 until you find more whitespace.
Discard the whitespace until you find the end of token 1. The rest of the line is all token 1.
convert token 2 and token 3 into numbers. I like to use strtol for this.
You can build all of the above or Daniel's answer (use his answer if at all possible) into an overload of operator>>. This lets you
mystruct temp;
while (filein >> temp)
{
// do something with temp. Stick it in a vector, whatever
}
The code to do this looks something like (Stealing wholesale from What are the basic rules and idioms for operator overloading? <-- Read this. It could save your life one day)
std::istream& operator>>(std::istream& is, mystruct & obj)
{
// read obj from stream
if( /* no valid object of T found in stream */ )
is.setstate(std::ios::failbit);
return is;
}
Here's another example of reading the file word by word. Edited to remove the example using the eof check as the while loop condition. Also included a struct as you mentioned that's what you just learned. I'm not sure how you're supposed to use your struct, so I just made it simple and had it contain 3 variables, a string, and 2 ints. To verify it reads correctly it couts the contents of the struct variables after its read in which includes printing out "New Jersey" as one word.
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h> // for atoi
using namespace std;
// Not sure how you're supposed to use the struct you mentioned. But for this example it'll just contain 3 variables to store the data read in from each line
struct tempVariables
{
std::string state;
int number1;
int number2;
};
// This will read the set of characters and return true if its a number, or false if its just string text
bool is_number(const std::string& s)
{
return !s.empty() && s.find_first_not_of("0123456789") == std::string::npos;
}
int main()
{
tempVariables temp;
ifstream file;
file.open("readme.txt");
std::string word;
std::string state;
bool stateComplete = false;
bool num1Read = false;
bool num2Read = false;
if(file.is_open())
{
while (file >> word)
{
// Check if text read in is a number or not
if(is_number(word))
{
// Here set the word (which is the number) to an int that is part of your struct
if(!num1Read)
{
// if code gets here we know it finished reading the "string text" of the line
stateComplete = true;
temp.number1 = atoi(word.c_str());
num1Read = true; // won't read the next text in to number1 var until after it reads a state again on next line
}
else if(!num2Read)
{
temp.number2 = atoi(word.c_str());
num2Read = true; // won't read the next text in to number2 var until after it reads a state agaon on next line
}
}
else
{
// reads in the state text
temp.state = temp.state + word + " ";
}
if(stateComplete)
{
cout<<"State is: " << temp.state <<endl;
temp.state = "";
stateComplete = false;
}
if(num1Read && num2Read)
{
cout<<"num 1: "<<temp.number1<<endl;
cout<<"num 2: "<<temp.number2<<endl;
num1Read = false;
num2Read = false;
}
}
}
return 0;
}

Read strings until end of lines

just to understand how to read correctly, how could i read the next text from file, if i want to read the diferent strings in each line. Each line can have different sizes (1st line could have 3 strings and 2nd line could have 100 strings)
2 //Number of lines
A AS BPST TRRER
B AS BP
I tried in my code something like this, but i dont know how to check if program it's in the end of line.
ifstream fich("thefile.txt");
fich >> aux; //Contain number of line
for(int i=0;i<aux;i++){ //For each line
string line;
getline(fich, line);
char nt; //First in line it's always a char
fich >> nt;
string aux;
while(line != "\n"){ //This is wrong, what expression should i use to check?
fich >> aux;
//In each read i'll save the string in set
}
}
So at the end, i want that set contains: {{A,AS,BPST,TRRER} {B,AS,BP}}
Thanks.
while(line != "\n"){ //This is wrong, what expression should i use to check?
Yes, because the '\n' was removed by the getline() function.
Using std::istringstream it is easy to parse an arbitrary number of words up to the end of the current line:
string aux;
std::istringstream iss(line);
while(iss >> aux) {
// ...
}
Also note:
fich >> aux; //Contain number of line
will leave you with an empty line read with std::getline() because in this case the '\n' will be leftover from that operation (see Using getline(cin, s) after cin for more detailed information).

Having trouble parsing simple text file separated by commas [duplicate]

I'm trying to read a list of items from from a file and then store them into a vector. The issue is my code is adding the last item to the vector twice and I'm not sure why the it keeps reading the file even though the program has reached the end.
Here's what's in the text file. The "Oranges" line appears twice when I display the contents of the vector.
Apples-pounds-10 2
Oranges-pounds-5 6
Here's the code
//Read the contents of the list to a file
while (!inputFile.fail())
{
//Extract the line from the list
getline(inputFile,item_name,'-');
getline(inputFile,item_unit,'-');
inputFile >> item_amount;
inputFile >> item_price;
//Create an instance of the item object
Item New_Item(item_name, item_unit, item_amount,item_price);
//Push it to the list vector
list.push_back(New_Item);
}
//Close the file
inputFile.close();
This is a typical symptom of the while (!infile.fail()) anti-pattern.
I'd define a struct and overload operator>> for that type:
struct item {
std::string name;
std::string unit;
int amount;
int price;
};
std::istream &std::operator>>(std::istream &is, item &i) {
getline(is, i.name, '-');
getline(is, i.unit, '-');
is >> i.amount;
return is >> i.price;
}
With those defined, reading the data borders on trivial:
std::ifstream inputFile("fileNameHere");
std::vector<New_Item> items { std::istream_iterator<Item>(inputFile),
std::istream_iterator<Item>() };
[I changed it from list to vector, because, well, you really don't want list. You can change it back, but probably shouldn't.]
The problem is that the "fail" flag is not set until you make an attempt at reading some more data from the file. Here is a quick way of fixing this:
for (;;) {
//Extract the line from the list
getline(inputFile,item_name,'-');
getline(inputFile,item_unit,'-');
inputFile >> item_amount;
inputFile >> item_price;
if (inputFile.fail()) break;
//Create an instance of the item object
Item New_Item(item_name, item_unit, item_amount,item_price);
//Push it to the list vector
list.push_back(New_Item);
}
If this is for a learning exercise, and you have not learn the >> operator yet, this should do it. Otherwise, the operator>> approach is better.

Every other line of data is skipped when reading and storing data from a file

I'm new to C++ and I'm having a little trouble when it comes to reading lines of data from a text file. Let's say I have an unknown number of lines in a text file, with each line in the same format: int string double . The only thing that will be definite is a space will separate each piece of data on a given line. I'm using an array of structs to store the data. The code below works great except that it skips a line of input after each loop. I've tried inserting various ignore() statements and still can't get it to read each line, only every other line. If I rewrite some of the getline statements at the end, then the wrong data starts getting stored for the variables after the first loop.
Text file might look like this:
18 JIMMY 71.5
32 TOM 68.25
27 SARAH 61.4
//code
struct PersonInfo
{
int age;
string name;
double height;
};
//..... fstream inputFile; string input;
PersonInfo *people;
people = new PersonInfo[50];
int ix = 0;
getline(inputFile, input, ' ');
while(inputFile)
{
people[ix].age = atoi(input.c_str());
getline(inputFile, input, ' ');
people[ix].name = input;
getline(inputFile, input, ' ');
people[ix].height = atof(input.c_str());
ix++;
getline(inputFile, input, '\n');
getline(inputFile, input, ' ');
}
I'm sure there are more advanced ways to do this, but like I said I'm pretty new to C++ so if there are just some slight modifications to the code above, that would be great. Thanks!
You can do the file reading as follows:
int ix = 0;
int age = 0;
string name ="";
double height = 0.0;
ifstream inputFile.open(input.c_str()); //input is input file name
while (inputFile>> age >> name >> height)
{
PersonInfo p ={age, name, height};
people[ix++] = p;
}
You've made this whole code ridiculously complicated.
struct PersonInfo
{
int age;
string name;
double height;
};
std::vector<PersonInfo> people;
PersonInfo newPerson;
while(inputFile >> newPerson.age >> newPerson.name >> newPerson.height)
people.push_back(std::move(newPerson));
Your problem is because first your read each bit of data one at a time from the file, then a whole line from teh file, then each bit of data one at a time from the file again. Maybe what you intended was more like this?
std::string fullline;
while(std::getline(inputFile, fullline)) {
std::stringstream linestream(fullline);
std::getline(linestream, datawhatever);
....
}
By the way, more idiomatic code might look like more like this:
std::istream& operator>>(std::istream& inputFile, PersonInfo& newPerson)
{return inputFile >> newPerson.age >> newPerson.name >> newPerson.height;}
{ //inside a function
std::ifstream inputFile("filename.txt");
typedef std::istream_iterator<PersonInfo> iit;
std::vector<PersonInfo> people{iit(inputFile), iit()}; //read in
}
Proof it works here

Using C++ ifstream extraction operator>> to read formatted data from a file

As my learning, I am trying to use c++ ifstream and its operator>> to read data from a text file using code below. The text file outdummy.txt has following contents:
just dummy
Hello ofstream
555
My questions is how to read char data present in the file into a char array or string. How to do this using the ifstream::operator>> in code below.
#include <iostream>
#include <fstream>
int main()
{
int a;
string s;
char buf[100];
ifstream in("outdummy.txt",ios_base::in);
in.operator>>(a); //How to read integer? How to read the string data.??
cout << a;
in.close();
getchar();
return 0;
}
If you want to use formatted input, you have to know in advance what data to expect and read it into variables of the according data type. For example, if you know that the number is always the fifth token, as in your example, you could do this:
std::string s1, s2, s3, s4;
int n;
std::ifstream in("outdummy.txt");
if (in >> s1 >> s2 >> s3 >> s4 >> n)
{
std::cout << "We read the number " << n << std::endl;
}
On the other hand, if you know that the number is always on the third line, by itself:
std::string line;
std::getline(in, line); // have line 1
std::getline(in, line); // have line 2
std::getline(in, line); // have line 3
std::istringstream iss(line);
if (iss >> n)
{
std::cout << "We read the number " << n << std::endl;
}
As you can see, to read a token as a string, you just stream it into a std::string. It's important to remember that the formatted input operator works token by token, and tokens are separated by whitespace (spaces, tabs, newlines). The usual fundamental choice to make is whether you process a file entirely in tokens (first version), or line by line (second version). For line-by-line processing, you use getline first to read one line into a string, and then use a string stream to tokenize the string.
A word about validation: You cannot know whether a formatted extraction will actually succeed, because that depends on the input data. Therefore, you should always check whether an input operation succeeded, and abort parsing if it doesn't, because in case of a failure your variables won't contain the correct data, but you have no way of knowing that later. So always say it like this:
if (in >> v) { /* ... */ } // v is some suitable variable
else { /* could not read into v */ }
if (std::getline(in, line)) { /* process line */ }
else { /* error, no line! */ }
The latter construction is usually used in a while loop, to read an entire file line by line:
while (std::getline(in, line)) { /* process line */ }
ifstream has ios_base::in by default. You don't need to specify it.
operator>> can be invoked directly as an operator: in >> a.
Reading strings is the same: in >> s, but the caveat is that it is whitespace-delimited, so it will read "just" by itself, without "dummy".
If you want to read complete lines, use std::getline(in, s).
Since you have elected to use C-strings, you can use the getline method of your ifstream object (not std::getline() which works with std::strings), which will allow you to specify the C-string and a maximum size for the buffer.
Based on what you had, and adding an additional buffer for the second line:
char buf[100];
char buf2[100];
in.getline(buf,sizeof(buf));
in.getline(buf2,sizeof(buf2));
in >> a;
However, as the other poster has proposed, try using the std::string and its methods, it will make your life easier.
You can read file contents and use a Finite State Machine for parsing.
Example:
void Parse(const char* buffer, size_t length);
size_t GetBufferSize();
size_t bufferSize = GetBufferSize();
char* buffer = new char[bufferSize];
std::ifstream in("input.txt");
while(in.getline(buffer, bufferSize)) {
Parse(buffer, in.gcount());
}
Alternatively, you can use a tool like Flex to write your parser.