I am doing a homework assignment where we are to read company data from a file and then process it for errors.
What I have so far I think will work with the first line, but I'm not sure how to make it read each line after. Each line is a record with ID, name, and payments. Basically I want to know how I can skip to the next line after I've processed the first. I haven't included the error checking yet but I think it will be in the last do while loop after 1 record is read. If the information read into each variable is wrong I can check it and output it to either the summary file or error file.
void processFile()
{
string filename;
ifstream recordFile;
ofstream summary("summary.dat");
ofstream error("error.dat");
cout << "Please enter a filename\n";
do
{
cin >> filename;
recordFile.open(filename);
if (!recordFile.is_open())
cout << "No file by that name. Please enter another filename\n";
}
while(!recordFile.is_open());
int ID = 0;
string firstName;
string lastName;
double payment1, payment2, payment3 = (0.00, 0.00, 0.00);
string numberOfRecords;
getline(recordFile, numberOfRecords);
do
{
ws(recordFile);
recordFile >> ID;
recordFile >> firstName;
recordFile >> lastName;
recordFile >> payment1;
recordFile >> payment2;
recordFile >> payment3;
}
while(!recordFile.eof());
}
*edit : I found part of my problem, I actually need to skip the first line and read on from that point. The first line in each file has useless data in it.
Use the getline function on the ifstream object
Two things. The first is if you're going to have to read multiple
records, and each record is a new line, the best solution is almost
always to read line by line, using std::getline, and then use
std::istringstream to break up the line (record) into the desired
fields. This has the advantage of keeping your input synchronized, even
in case of errors; you don't have to worry about how much to skp ahead
or ignore.
The second point is that you're checking for eof(). This is almost
always an error; sometimes, it will lead you to reading one line too
many, and in other cases, of ignoring the last line or field. If the
input is successful (and you can only check for end of file after
trying to input beyond it), the stream will behave as true in a
conditional context; if not, it will behave as false. So your loop
should be:
std::string line;
while ( std::getline( recordFile, line ) ) {
std::istringstream record( line );
record >> ID;
if ( ! record ) ...
// ...
}
And one final comment: all of the >> operators strip leading spaces,
so you don't need your call to ws. On the other hand, with the above
schema, you might want to do something like:
if ( record >> ws && record.get() != EOF ) {
// Unexpected garbage at end of line...
}
at the very end of your loop, to verify that there isn't extra text.
Related
I'm trying to read in a file that should contain only numbers in it. I can successfully read in the entire file if it meets that criteria, but if it so happened to have a letter in it, I need to return false with an error statement.
The problem is I'm finding it hard for my program to error when it finds this character. It can find it no problem, but when it does, it decides to just skip over it.
My code to read in the file and attempt to read in only numbers:
bool compute::Read (ifstream& stream)
{
double value;
string line;
int lineNumber = 1;
if (stream)
{
while (getline(stream, line))
{
lineNumber++;
istringstream strStream(line);
while (strStream >> value)
{
cout << value << endl;
}
}
}
return true;
}
The input file which I use for this is
70.5 61.2 A8 10.2
2
Notice that there is a non-number character in my input file. It should fail and return false at that point.
Currently, all it does is once it hits the "A", it simply returns to the next line, continuing the getline while loop.
Any help with this would be much appreciated.
The stringstream does catch those errors, but you're doing nothing to stop the enclosing loop from continuing when an error is found. You need to tailor your main loop so that it stops when the stringstream finds an error, which you can't do if the stringstream is being reconstructed on each iteration. You should create a for() loop instead and construct the stringstream in the declaration part. And the condition to the loop should be "as long as the stringstream and stream do not catch an error". For example:
for (std::istringstream iss; iss && std::getline(stream, line);)
{
iss.clear();
iss.str(line);
while (iss >> value)
{
std::cout << value << '\n';
}
}
Futhermore, it doesn't look like you need to use std::getline() or std::istringstream if you just want to print each value. Just do:
while (stream >> value) {
std::cout << value << '\n';
}
The above will stop when it finds an invalid character for a double.
You need the code to stop streaming but return false if it hasn't yet reached the end of the "input".
One way, possibly not the most efficient but still one way, to do that is parse a word at a time.
If you read first into a std::string and if it works (so the string is not empty) create an istringstream from that string, or reuse an existing one, and try streaming that into a double value.
If that fails, you have an invalid character.
Of course you can read a line at a time from the file, then split that into words, so that you can output a meaningful error message showing what line the bad text was found.
The issue of reading straight into doubles is that the stream will fail when it reaches end of file.
However it is possible to workaround that too because the reason for failing has an error status which you can check, i.e. you can check if it eofbit is set. Although the f in eofbit stands for "file" it applies to any stream not just files.
Although this method may sound better than reading words into a string first, I prefer that method in normal circumstances because you want to be able to report the error so you'll want to print in the error what was read.
First of all, there's an data inside my file...
1|Malaysia|UK|One-Way|20|6|20|6|2000|12|12|12|12|12|
The above data is the data inside my file
But when I cout the data, there's an duplicate data...
1|Malaysia|UK|One-Way|20|6|20|6|2000|12|12|12|12|12|
1|Malaysia|UK|One-Way|20|6|20|6|2000|12|12|12|12|12|
So what's the problem when I cout the data???
Here's the code....
void Flight::displayFlight(vector <Flight> &flightProfile)
{
string readFlightID, readPrice, readBusinessList, readBusinessWaitingList, readEconomicList, readEconomicWaitingList;
flightProfile.erase(flightProfile.begin(),flightProfile.end());
ifstream inFlight("Flight.txt");
if(inFlight.fail()) return;
while(!(inFlight.eof()))
{
getline(inFlight,readFlightID,'|');
istringstream(readFlightID)>>flightID;
getline(inFlight,departure,'|');
getline(inFlight,destination,'|');
getline(inFlight,flightType,'|');
getline(inFlight,readBusinessList,'|');
istringstream(readBusinessList)>>businessList;
getline(inFlight,readBusinessWaitingList,'|');
istringstream(readBusinessWaitingList)>>businessWaitingList;
getline(inFlight,readEconomicList,'|');
istringstream(readEconomicList)>>economicList;
getline(inFlight,readEconomicWaitingList,'|');
istringstream(readEconomicWaitingList)>>economicWaitingList;
getline(inFlight,readPrice,'|');
istringstream(readPrice)>>price;
getline(inFlight, day,'|');
getline(inFlight, month,'|');
getline(inFlight, year,'|');
getline(inFlight, hour,'|');
getline(inFlight, min,'|');
inFlight.ignore(numeric_limits<streamsize>::max(), '\n');
cout<<flightID<<departure<<destination<<flightType<<businessList<<businessWaitingList<<economicList<<economicWaitingList<<price<<day<<month<<year<<hour<<min<<endl;
}
inFlight.close();
}
Your (and others) common mistake is that the eof-bit is only set on input operations (RTFM).
The correct way to read a file line by line would be to do:
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
cout << line << endl;
}
myfile.close();
}
You always need to check after you read from a stream if the stream is in a good state! If reading of the data failed, e.g., because the end of the stream was reached, the stream will be in a fail state. For example
while (std::getline(inFlight, readFlightID)
&& std::istringstream(readFlightId) >> flightID
&& std::getline(inFlight, departure)
...
) {
// now process the read data
}
BTW, note that the trick using a temporary stream only works like this if the target type is a member of std::istream. If it is not, you'll need to extra a reference from the stream, e.g. using
std::istringstream("some text") >> std::skipws >> value
That's because you're not checking that your getline succeeds.
The last time through the loop, it probably fails (because
you've read all of the data), so you pick up the old values.
This is not the way to read line based input. For starters,
line based input should be read line by line:
std::string line;
while ( std::getline( inFlight, line ) ) {
// Parse line here...
}
There are many ways to parse the line. One of the most common
solutions is to put it into an std::istringstream and read
from that. That's probably overkill for what you're doing,
however; you need probably something like boost::split (which
you can knock up in less than an hour if you can't use Boost).
At any rate, while ( !someIStream.eof() ) is never correct.
Two other quick comments: you shouldn't define your variables
before you need them, and there's no real point in closing
inFlight if it's immediately going out of scope.
I try to get from the user inputs till blank line
so I wrote this:
while (c != '\n')
{
c = cin.peek();
cin >> check;
if (check == "Test")
{
cin >> ID >> One >> Two >> Three;
Test[i++] = Test(ID, One, Two, Three);
}
}
to example, I get from the user Test 12 45 56 78 99 now, check=test, id=45, one, 56, two=78, three=99
and when I enter empty line, why the while loop isn't stopped?
It's not clear what you're trying to do; you don't use the
results of cin.peek() until far later, after having done
significantly more input, without testing whether it succeeded
or not. Given the code, my first question is: do you understand
how while works? Modifying the value of a control variable
within the loop will not cause you to break out of the loop; the
test is only done at the top of the loop. And you must always
verify that input has succeeded before using the variables
you've input.
If your input is line oriented, the classical solution would
be:
std::string line;
while ( std::getline( std::cin, line ) && !isEmpty( line ) ) {
std::istringstream parser( line );
if ( parser >> check >> ID >> One >> Two >> Three >> std::ws
&& parser.get() == EOF ) {
// Data is good, can be used...
} else {
// Some sort of format error in the line...
}
}
I've put the test for an empty line in a separate function,
because you probably want to treat a line with just white space
as empty. (Users may accidentally hit the space bar before
enter, and what they see will still look like an empty line.)
That's also why I >> into std::ws before checking that
there's no garbage at the end of line when parsing.
cin >> check;
is a formatted input function, meaning it will discard leading white space. A blank line is just leading white space, it will be discarded and the extraction operator will keep reading until non-whitespace data arrives, or an I/O error occurs.
I have managed to skip the name section when reading values from a file with name and value pairs. But is there another way to skip the name section without declaring a dummy string to store the skipped data?
Example text file: http://i.stack.imgur.com/94l1w.png
void loadConfigFile()
{
ifstream file(folder + "config.txt");
while (!file.eof())
{
file >> skip;
file >> screenMode;
if (screenMode == "on")
notFullScreen = 0;
else if (screenMode == "off")
notFullScreen = 1;
file >> skip;
file >> playerXPosMS;
file >> skip;
file >> playerYPosMS;
file >> skip;
file >> playerGForce;
}
file.close();
}
You can use std::cin.ignore to ignore input up to some specified delimiter (e.g., a new-line, to skip an entire line).
static const int max_line = 65536;
std::cin.ignore(max_line, '\n');
While many people recommend specifying a maximum of something like std::numeric_limits<std::streamsize>::max(), I do not. If the user accidentally points the program at the wrong file, they shouldn't wait while it consumes an inordinate amount of data before being told something's wrong.
Two other points.
Don't use while (!file.eof()). It mostly leads to problems. For a case like this, you really want to define a struct or class to hold your related values, define an operator>> for that class, then use while (file>>player_object) ...
The way you're reading right now really tries to read a "word" at a time, not a whole line. If you want to read a whole line, you probably want to use std::getline.
The system did this:
Please input the Full Name of the user:Please input the Full Name of the user:
It output the string "Please input the Full Name of the user:" twice , how do i change the code to make it just cout once
string fullname = "";
do
{
cout << "Please input the Full Name of the user: ";
getline (cin,fullname);
}while(fullname.length()<1);
C++ What is causing the system to output twice
You could try flushing your input stream to get rid of leftover newlines:
std::cin.ignore(x);
(with x being the number of characters to ignore, e.g. INT_MAX).
Simple solution would be to move the std::cout statement outside of the do-while loop.
string fullname = "";
cout << "Please input the Full Name of the user: ";
do
{
getline (cin,fullname);
}while(fullname.length()<1);
You are performing an input operation without checking the result, which is a hard programming and understanding error. Do this instead:
for (std::string line; ; )
{
std::cout << "Name: ";
if (!std::getline(std::cin, line) || !line.empty()) { break; }
}
The first condition checks whether the input succeeded (which is false when the input stream is closed), and the second checks whether the read line is non-empty. The short-circuit semantics of || make the second check legal.
As others have pointed out the problem is that you have an extra '\n' character on the input stream.
Contrary to the popular answer I don't think flushing (ignore()) the current input is a good solution. You are treating the symptom not the problem. If you are using ignore() you are potentially throwing away user input that you might actually want or something that could have detected an error from the user:
> Input Your age
> 36xxxx
// If you use
std::cin >> age;
// Then sometime later in your code you use
// ignore to make sure that you have "correctly" skipped to the next new line
std::ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// You have now just ignored the fact that the user typed xxx on the end of the input.
// They were probably doing that to force an error in the code or something else erroneous
// happened but you just missed it with std::ignore()
The best solution is not to get into this situation.
This problem is caused by using a combination of operator<<() and std::getline() to parse user input. I love using operator<<() to parse normal or regular input; but manual user input (ie Question/Answer) is harder to predict and users input is line based (there input ends at the '\n' character because the buffer is flushed when they hit <enter>).
As a result when I parse manual user input I always use std::getline(). This way I know I got the whole of their answer. It also allows me to validate the input to make sure there was not a typing error.
std::cout << "What is your age\n";
std::string line;
std::getline(std::cin, line); // Get user input
// Use stream for easy parsing.
std::stringstream linestream(line);
// Get value we want.
linestream >> age;
// Validate that we have not thrown away user input.
std::string error;
linestream >> error;
if (error.length() != 0) { /* do stuff to force user to re-input value */ }