Reading text file: Read multiple values if present - c++

I am having issues trying to get the program to read to the end of a line in a text file.
I am trying to read data from a text file (one item per line) with the following format (space-separated fields):
house (12345)
type (A = Auto or M = motorcylce)
license (WED123)
year (2012)
msrp (23443)
The data will be used to calculate vehicle registration total.
At the moment the program is reading all the lines which are formatted as above, however a house could have more than one vehicle, therefore there is additional data on the line (all but the first fields are repeated in this case).
For example:
111111 A QWE123 2012 13222 M RTW234 2009 9023
// ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
// first vehicle second vehicle
Once I reach a line which has the additional data, the program does not read it and goes into an infinite loop. How can I read the additional data in the line to get to the end of the file and stop the program from an infinite loop.
#include <stdlib.h>
#include <iostream>
#include <fstream>
using namespace std;
int main () // Function Header
{ // Start Function
int house;
char type;
string license;
int year, msrp ;
char ch;
ifstream inData;
ofstream outData;
inData.open("register.txt");
outData.open("vehicle.txt");
inData >> house; // Priming Read
while (inData) { // Test file stream variable
do {
inData >> type;
inData >> license;
inData >> year;
inData >> msrp;
outData << house << type << license << year << msrp << endl;
ch = inData.peek();
inData >> house;
} while(ch != '\n'); // Check for end of line
} // End while
system ("Pause");
return 0;
}

Your program will have a hard time detecting the end of the line. When it tries to read the "extra data" but encounters the next line instead, an error occurs on the stream, preventing you from being able to read again.
You can "fix" your program by not reading the house value in the inner loop. Instead, read it after you have detected the end of line.
ch = inData.peek();
//inData >> house; // WRONG: house might be next vehicle type
} while(ch != '\n'); // Check for end of line
inData >> house; // CORRECT
} // End while
However, a better way to handle this may be to use getline and istringstream. First get an entire line of input with getline. Place the input into an istringstream. Then, get the rest of the data from that. See M. M.'s version for an illustration of this.

Related

Read in file with tabs and spaces

I have a text file in this format:
Petroleum Engineering 94600 175500
Marine Engineering 73900 123200
Economics and Mathematics 60000 122900
Geophysics 54100 122200
Cognitive Science 54000 121900
What I have is course name, average early career pay, and mid career pay, all separated by tabs. The course names with multiple words are separated by spaces.
I want to read the course name and put it in one variable, the first pay in a second variable, and the third pay in a third variable.
int main(){
ifstream in;
in.open("Salaries.txt");
string course;
int mid;
int avg;
if (in.fail()){
cout<< "failed to open file";
exit(1);
}
while(!in.eof()){
in >> course >> avg >> mid;
cout << course<<endl;
}
in.close();
}
When I compile this code, it outputs nothing, and the program does not exit or terminate.
Someone in comments pointed out that using eof() is not recommended, so I tried thus instead:
while(in >> course >> sm >> lg){
cout << course << endl;
}
The process exits without outputting anything to the screen. I tried it on a input file that looks something like this:
NCORES 1
NEW 0
CORE 100
INPUT 5000
CORE 20
And it takes the string and puts it into one variable, and takes the number and puts it into another variable, and prints the correct output. So the problem is the white space between the words in the cours name in the original file, and I don't know how to account for that.
Although your code does have other problems, the problem you're running into is from the fact that operator>> for a string stops reading at the first white space it encounters. That means on the first line, it reads Petroleum into course, then tries to read Engineering into avg. Since avg is a number, that doesn't work, so the conversion fails. From there, all further attempts at reading from the stream fail.
To fix that, you probably want to use std::getline to read the course name. It allows you to specify the character that will end the string it reads. In this case, you apparently want to pass a tab character ('\t') for that parameter.
If I were doing it, I'd probably put the three items into a struct, and overload operator>> for that struct:
struct course {
std::string name;
long early_pay;
long mid_pay;
friend std::istream &operator>>(std::istream &is, course &c) {
if (std::getline(is, c.name, '\t')) {
std::string temp;
std::getline(is, temp, '\t');
c.early_pay = std::stol(temp);
std::getline(is, temp);
c.mid_pay = std::stol(temp);
}
return is;
}
};
Then reading the data and printing out the course names would look something like this:
int main() {
std::istringstream input(
R"(Petroleum Engineering 94600 175500
Marine Engineering 73900 123200
Economics and Mathematics 60000 122900
Geophysics 54100 122200
Cognitive Science 54000 121900 )");
course c;
while (input >> c)
std::cout << c.name << '\n';
}
Use std::getline() to read lines from the file, and use std::istringstream to parse each line. You can then use std::getline() to read tab-delimited strings from each line. For example:
int main() {
ifstream in("Salaries.txt");
if (!in.is_open()) {
cout<< "failed to open file";
exit(1);
}
string line course;
int mid, avg;
while (getline(in, line)) {
istringstream iss(line);
getline(iss, course, '\t');
iss >> avg >> mid;
cout << course << endl;
}
return 0;
}

Reading in from file gives unexpected output

I am working on reading in from a file and parsing through data from command line argument for homework. And I ran in a wall and I do not know what's the problem, and I hope I could get some advice on what I am missing.
The data file is composed thusly; on the first line, it has number of total lines. For each line after that, it is a line of string separated by | character. I need the '|' character because I want to split my string into substrings.
Here is an example of input file.
3
league of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
Here is my code.
int main( int argc, char* const argv[] )
{
//change string to char* so I can check through each char to see if the
//thing I read in is '|' character.
String Data = (argv[1]);
ifstream fin (Data.c_str());
//check whether the file is open.
if ( !fin.is_open() )
{
cout << "Could not open file" << endl;
}
else
{
int dataLines;
char dataBuffer[100];
//The first integer I read in will be how many lines I will loop through
fin >> dataLines;
//ignore the new line character and do not include it in the count of
//dataLines.
fin.ignore();
//use noskipws so I can recognize whitespaces.
fin >> noskipws >> dataBuffer;
//TEST CODE: COMMENTED OUT FOR NOW.
//cout<<dataBuffer<<endl;
//loop for the number of lines
for(int i = 0; i < dataLines; i++)
{
fin.getline(dataBuffer, 100);
//print the buffer for checking
cout<<dataBuffer<<endl;
}
}
//close the file.
fin.close();
return 0;
}
The result is supposed to look like this.
league of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
The actual result looks like this
of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
The first word that I read in from buffer is gone. "league" is the one that is missing, and I tried to see what the problem is by inserting the test code at the location specified in my code. With the given test code, my output is
league
of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
So the problem is that between reading in the file with noskipws and the forloop that loops over dataLine. Before the forloop my buffer is league. Yet once I enter the loop it is passed that and goes straight to of.
What am I missing here? What could be a possible solution?
Main problem:
fin >> noskipws >> dataBuffer;
Does two things. 1. >> noskipws turns off automatically skipping whitespace, unnecessary because of how OP is reading the stream. 2. >> dataBuffer reads the first word from the stream, in this case consuming the word "league"
Solution: Don't do this.
Other problems:
fin.ignore();
will ignore exactly one character. But what if someone left a nigh-invisible space after the count? Instead use
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
to ensure the rest of the line is consumed in its entirity.
char dataBuffer[100];
Why make yourself suffer? Instead use
std::string dataBuffer;
Recommendation:
Use std::stringstream and std::getline to tokenize the lines on '|'
std::stringstream stream(databuffer);
std::string token;
while (std::getline(stream, token, '|')
{
std::cout << token << ',';
}
You do not need the following line:
fin >> noskipws >> dataBuffer;
Tested with g++ 4.8.3 2 on RHEL 7.1
Thanks User 4581301. It reads in the data correctly and split with '|' character. Now I can work on storing the data into classes.
for anyone who may have same problem, this is the fixed up version of code.
int main( int argc, char* const argv[] )
{
String Data = (argv[1]);
ifstream fin (Data.c_str());
if ( !fin.is_open() )
{
cout << "Could not open file" << endl;
}
else
{
int dataLines;
char dataBuffer[100];
fin >> dataLines;
fin.ignore();
for(int i = 0; i < dataLines; i++)
{
while(fin.getline(dataBuffer, 100, '|'))
{
cout<<dataBuffer<<endl;// check to see if it reads in correctly.
}
}
}
fin.close();
return 0;
}

Four variable file I/O C++ into hash table for processing

I have a problem with C++ File input.
I have a file with n lines that each lines contains 4 variables. I need them read them into a hash table that I created. My problem is that I can't read the file correct way.
For example here the input file variables:
line id cont uniq-number Band
0 10 B 02020213456 DaftPunk
1 11 A 02030213456 Dazy
and so on..
The main problem is to read each variable in line until file EOF.
So I need read in each line these variables id, cout, uniq-number and band and while it is reading put these data inside a hash table to process them even further in C++.
Example
cout << "File" << endl;
int date,id;
string group,line,ch;
datar d;
hasher h1;
ifstream inFile;
inFile.open("file.txt");
while (getline (inFile,line))
{
// "reads" file each variable
inFile >> id >> ch >> date >> group;
//add these variable in line one to first hash line
d.id = id;
d.data = ch;
d.date= date;
d.group = group;
h1.add(d);
//must repeat until file EOF for each line
}
inFile.close();
In this code,
while (getline (inFile,line))
{
// "reads" file each variable
inFile >> id >> ch >> date >> group;
//add these variable in line one to first hash line
d.id = id;
d.data = ch;
d.date= date;
d.group = group;
h1.add(d);
//must repeat until file EOF for each line
}
the getline reads a line.
Then the loop body reads items from the next line.
Instead of that
inFile >> id >> ch >> date >> group;
do e.g.
istringstream linestream( line );
linestream >> id >> ch >> date;
getline( linestream, group );
The last getline in order to handle possible spaces in a name.
This assumes that the name is last on the line, as it currently is, and it assumes that it's not the case that every second line should be ignored (i.e., that that was bug).
It's also a good idea to add failure checking.
If a stream operation fails then the stream enters a failure state, and you can check that via the member function .fail().

Reading in from a txt file. Trouble parsing info

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);
}

why its jumping out of program?

I just start learning C++.
When I execute my code it's jumping out of the program without any error. Why?
#include "stdafx.h"
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char s1[20],s2[10];
cout<<" enter a number : ";
cin.get(s1,19);
cout<<" enter a number : ";
cin.get(s2,9);
cout<<s1<<"/n"<<s2;
getch();
}
The method get() reads upto the '\n' character but does not extract it.
So if you type: 122345<enter>
This line:
cin.get(s1,19);
Will read 12345, but the '\n' (created by hitting <enter>) is left on the input stream. Thus the next line to read:
cin.get(s2,9);
Will read nothing as it sees the '\n' and stops. But it does not extract the '\n' either. So the input stream still has the '\n' there. So this line:
getch();
Just reads the '\n' character from the input stream. Which then allows it to finish processing and exit the program normally.
OK. That is what is happening. But there is more to this. You should not be using get() to read formatted input. Use the operator >> to read formatted data into the correct type.
int main()
{
int x;
std::cin >> x; // Reads a number into x
// error if the input stream does not contain a number.
}
Because the std::cin is a buffered stream the data is not sent to the program until you push <enter> and the stream is flushed. Thus it is often useful to read the text (from user input) a line at a time then parse that line independently. This allows you to check the last user input for errors (on a line by line bases and reject it if there are errors).
int main()
{
bool inputGood = false;
do
{
std::string line;
std::getline(std::cin, line); // Read a user line (throws away the '\n')
std::stringstream data(line);
int x;
data >> x; // Reads an integer from your line.
// If the input is not a number then data is set
// into error mode (note the std::cin as in example
// one above).
inputGood = data.good();
}
while(!inputGood); // Force user to do input again if there was an error.
}
If you want to get advanced then you can also look at the boost libs. They provide some nice code in general and as a C++ program you should know the contents of boost. But we can re-write the above as:
int main()
{
bool inputGood = false;
do
{
try
{
std::string line;
std::getline(std::cin, line); // Read a user line (throws away the '\n')
int x = boost::lexical_cast<int>(line);
inputGood = true; // If we get here then lexical_cast worked.
}
catch(...) { /* Throw away the lexical_cast exception. Thus forcing a loop */ }
}
while(!inputGood); // Force user to do input again if there was an error.
}
You need to use cin.ignore(); to ignore the newline character from stream before getting next input.