c++ while loops, using getline function - c++

Ok so to start because people like to know, this is homework....
I am writing a C++ Program to convert a text file to XML (this is not where I need help)
I am having this issue.
I am writing a while loop to cycle through the companies in the txt file (up to 15) and at the end of each group it is marked with the words --END_MANAGER_DATA--. I am trying to write a while loop that will loop through the company's and finish looping when it reaches --END_MANAGER_DATA--. below is an example of the txt file
19936 WALKER KOLTON PORTLAND TN
HARMAN INTERNATIONAL INDUSTRIES INC
LUCENT TECHNOLOGIES INC
COMPUTER SCIENCES CORP
COMMUNICATIONS CORPORATION
--END_MANAGER_DATA--
this is the code I am trying to use.....
getline(inFile,company);
inFile.ignore();
while(company != "--END_MANAGER_DATA--"|| !inFile.eof())
{
outputfile <<"\t\t\t <company> "<<company << "</company>"<<endl;
getline(inFile,company);
inFile.ignore();
}
this is not working....it just stays in the loop.... can someone offer advice as to a route I can take. I am not asking for you to finish my homework....just need a nudge in the right direction

The idiomatic way to read data is to put the std::getline() into the while loop's test condition rather like this:
std::string line;
while(std::getline(inFile, line) && line != "--END_MANAGER_DATA--")
{
std::cout << "\t\t\t<company>" << line << "</company>" << '\n';
}
This works because the while will only proceed if there were no errors reading the line. Testing for eof() doesn't work because eof() doesn't happen until after the read has taken place and the read will only take place if no error condition already exists.
In your code you ignore() the first character of each line which is why, I think, you can never match "--END_MANAGER_DATA--".

Try this:
string company = "";
while(company.compare("--END_MANAGER_DATA--") != 0 && !inFile.eof()) {
getline(inFile, company);
outFile << "\t\t\t <company> "<< company << "</company>" << endl;
inFile.ignore();
}

Related

Is there a way to getline just part of a line?

I couldn't come up with a better title, but let me explain.
I have a file like this
potions.txt potions
ingredients.txt ingredients
INSERT((Red Mountain Flower|0.1|2|Yes|No|Mountains,Forests),ingredients)
INSERT((Abecean Longfin|0.5|15|No|Yes|Rivers,Lakes),ingredients)
INSERT((48|Glibness|Fortify|+20 Speechcraft for 60 seconds.|96|None|None),potions)
UPDATE((Abecean Longfin|0.5|15|No|Yes|Rivers,Lakes,Swamps),ingredients)
UPDATE((205|Minor Healing|Health|Restore 25 points of Health.|17|Blue Mountain Flower|Charred Skeever Hide),potions)
UPDATE((206|Healing|Health|Restore 50 points of Health.|36|Blue Mountain Flower|Swamp Fungal Pod),potions)
SELECT((9|*|*|*|*|*|*),potions)
INSERT((Purple Mountain Flower|0.1|2|Yes|No|Mountains,Forests),ingredients)
I am trying to parse the file to store the proper things into the proper variable.
So, I tried writing
for(int i = 0; i < num_of_lines; i++)
{
getline(inputFile, insert, '(');
if(insert == "INSERT")
{
cout << insert << endl;
}
}
And I immediately know my problem. When the for loop continues, the order it will read things in is
(
Red Mountain Flower|0.1|2|Yes|No|Mountains,Forests),ingredients)INSERT(
(Abecean Longfin|0.5|15|No|Yes|Rivers,Lakes),ingredients)INSERT(
Meaning it will never get another "insert" to read in so i'll never have access to it in order to parse the file further.
Is there a way to getline just part of a line so that If the string matches, I can continue to parse the file? I've tried find functions, I've tried string compare functions, but I can't seem to get anything to work. Any suggestions on how i can solve this would be appreciated.
The input is obviously line-based, so read it line by line and then parse those lines:
for(int i = 0; i < num_of_lines; i++)
{
getline(inputFile, lineText);
std::istringstream line(lineText);
// Now, work with `line` as your stream
getline(line, insert, '(');
if(insert == "INSERT")
{
cout << insert << endl;
}
}

C++ reading a file into a struct

Using fstreams I have a file opened that contains numerous lines. Each contiguos set of 4 lines are such that: the first line is an int, the second and third are strings and fourth is a double. This sequence continues till EOF.
I'm attempting to load these lines into a struct array:
struct Library {
int id;
string title;
string artist;
double price;
};
and the code I'm trying to implement to load data into the struct is this:
const int LIMIT = 10
Library database[LIMIT];
ifstream file;
file.open("list.txt");
if(file) {
while(!(file.eof()) && counter < LIMIT) {
file >> database[counter].id;
getline(file, database[counter].title;
getline(file, database[counter].artist;
file >> database[counter].price;
}
} else {
...
}
// Using the following to debug output
for(int i = 0; i < counter; i++) {
cout << "ID: " << database[i].id << endl
<< "Title: " << database[i].title << endl
<< "Artist: " << database[i].artist << endl
<< "Price: " << database[i].price << endl
<< "-----------------------" << endl;
}
The file I'm trying to throw at this thing is
1234
Never Gonna Give You Up
Rick Astley
4.5
42
Thriller
Michael Jackson
32.1
The problem I'm having here is that between reading the id and title using file >> ... and getline(...) is that somewhere a newline bite is being introduced screwing up the output, which displays this monstrosity...
ID: 1234
Title:
Artist: Never Gonna Give You Up
Price: 0
--------------------
ID: 0
Title:
Artist:
Price: 0
--------------------
The solution is probably the most basic of solutions, but mainly because I can't figure out exactly what is going on with the newline bite I can't combobulate a phrase to shove into google and do my stuff there, and I'm at the stage where I've been looking at a problem so long, basic knowledge isn't working properly - such as how to handle basic input streams.
Any form of help would be much appreciated! Thanks in advance :)
This happens because the >> operator for the input stream only grabs part of a line, and does not always grab the newline character at the end of the line. When followed by a call to getline, the getline will grab the rest of the line previously parsed, not the line after it. There are a few ways to solve this: you can clear the buffer from the input stream after each read, or you can simply get all your input from getline and just parse the resulting strings into an integer or a double when you need to with calls to stoi or stod.
As a side note, you don't want to detect the end of your file the way you presently are. See why is eof considered wrong inside a loop condition?
You can solve this problem by adding:
fflush(file);
everytime before you use getline(file, ...). Basically this will clear the input buffer before you use the getline() function. And fflush() is declared in the cstdio library.
file >> database[counter].id;
will read, in this case, a whitespace separated sequence of characters that is interpreted as an int. The newline is considered whitespace. You should now be sitting on that newline character, thus the getline() will read nothing -- successfully -- and increment the file position just past that.
You may be better off using getline() for each line and then separately interpreting the lines from the reading. For example, the first line read could be interpreted with a subsequent std::stoi() to get the integer representation from the string.

How to extract specific substring from getline function in C++?

I'm fairly new to C++ so please forgive me if my terminology or methodology isn't correct.
I'm trying to write a simple program that:
Opens two input files ("infileicd" and "infilesel").
Opens a single output file "list.txt".
Compares "infilesel" to "infileicd" line by line.
If a line from "infilesel" is found in "infileicd", it writes that line from "infileicd" to "list.txt", effectively making a separate log file.
I am using the getline() function to do this but have run into trouble when trying to compare each file line. I think it might be easier if I could use only the substring of interest to use as a comparison.
The problem is that there are multiple words within the entire getline string and I am only really interested in the second one. Here are two examples:
"1529 nic1_mau_op_mode_3 "8664afm007-01" "1" OUTPUT 1 0 LOGICAL 4 4136"
"1523 pilot_mfd_only_sel "8664afm003-02" "1" OUTPUT 1 0 LOGICAL 4 4112"
"nic1_mau_op_mode_3" and "pilot_mfd_only_sel" are the only substrings of interest.
It would make it a lot easier if I could only use that second substring to compare but I don't know how to extract it specifically from the getline() function. I haven't found anything suggesting it is impossible to do this, but if it is impossible, what would be an alternative method for extracting that substring?
This is a personal project so I'm under no time contstraints.
Any assistance is greatly apprecated in advance. Here is my code (so far):
int main()
{
//Open the file to write the selected variables to.
ofstream writer("list.txt");
//Open the selected variabels file to be read.
ifstream infilesel;
infilesel.open("varsel.txt");
//Open the icd file to be read.
ifstream infileicd;
infileicd.open("aic_fdk_host.txt");
//Check icd file for errors.
if (infileicd.fail()){
cerr << "Error opening icd.\n" << endl;
return 1;
}
else {
cout << "The icd file has been opened.\n";
}
//Check selected variables file for errors.
if (infilesel.fail()){
cerr << "Error opening selection file.\n" << endl;
return 1;
}
else {
cout << "The selection file has been opened.\n";
}
//Read each infile and copy contents of icd file to the list file.
string namesel;
string nameicd;
while(!infileicd.eof()){
getline(infileicd, nameicd);
getline(infilesel, namesel);
if (nameicd != namesel){ //This is where I would like to extract and compare the two specific strings
infileicd; //Skip to next line if not the same
} else {
writer << nameicd << namesel << endl;
}
}
writer.close();
infilesel.close();
infileicd.close();
return 0;
}
So, based on what we discussed in the comments, you just need to toss the stuff you don't want. So try this:
string namesel;
string nameicd;
string junk;
while(!infileicd.eof()){
// Get the first section, which we'll ignore
getline(infileicd, junk, ' ');
getline(infilesel, junk, ' ');
// Get the real data
getline(infileicd, nameicd, ' ');
getline(infilesel, namesel, ' ');
// Get the rest of the line, which we'll ignore
getline(infileicd, junk);
getline(infilesel, junk);
Basically, getline takes a delimiter, which by default is a newline. By setting it as a space the first time, you get rid of the first junk section, using the same method, you get the part you want, and then the final portion goes to the end of the line, also ignoring it.

How to know if the next character is EOF in C++

I'm need to know if the next char in ifstream is the end of file. I'm trying to do this with .peek():
if (file.peek() == -1)
and
if (file.peek() == file.eof())
But neither works. There's a way to do this?
Edit: What I'm trying to do is to add a letter to the end of each word in a file. In order to do so I ask if the next char is a punctuation mark, but in this way the last word is left without an extra letter. I'm working just with char, not string.
istream::peek() returns the constant EOF (which is not guaranteed to be equal to -1) when it detects end-of-file or error. To check robustly for end-of-file, do this:
int c = file.peek();
if (c == EOF) {
if (file.eof())
// end of file
else
// error
} else {
// do something with 'c'
}
You should know that the underlying OS primitive, read(2), only signals EOF when you try to read past the end of the file. Therefore, file.eof() will not be true when you have merely read up to the last character in the file. In other words, file.eof() being false does not mean the next read operation will succeed.
This should work:
if (file.peek(), file.eof())
But why not just check for errors after making an attempt to read useful data?
file.eof() returns a flag value. It is set to TRUE if you can no longer read from file. EOF is not an actual character, it's a marker for the OS. So when you're there - file.eof() should be true.
So, instead of if (file.peek() == file.eof()) you should have if (true == file.eof()) after a read (or peek) to check if you reached the end of file (which is what you're trying to do, if I understand correctly).
For a stream connected to the keyboard the eof condition is that I intend to type Ctrl+D/Ctrl+Z during the next input.
peek() is totally unable to see that. :-)
Usually to check end of file I used:
if(cin.fail())
{
// Do whatever here
}
Another such way to implement that would be..
while(!cin.fail())
{
// Do whatever here
}
Additional information would be helpful so we know what you want to do.
There is no way of telling if the next character is the end of the file, and trying to do so is one of the commonest errors that new C and C++ programmers make, because there is no end-of-file character in most operating systems. What you can tell is that reading past the current position in a stream will read past the end of file, but this is in general pretty useless information. You should instead test all read operations for success or failure, and act on that status.
You didn't show any code you are working with, so there is some guessing on my part. You don't usually need low level facilities (like peek()) when working with streams. What you probably interested in is istream_iterator. Here is an example,
cout << "enter value";
for(istream_iterator<double> it(cin), end;
it != end; ++it)
{
cout << "\nyou entered value " << *it;
cout << "\nTry again ...";
}
You can also use istreambuf_iterator to work on buffer directly:
cout << "Please, enter your name: ";
string name;
for(istreambuf_iterator<char> it(cin.rdbuf()), end;
it != end && *it != '\n'; ++it)
{
name += *it;
}
cout << "\nyour name is " << name;
just use this code in macosx
if (true == file.eof())
it work for me in macosx!

Reading a ";" delimited file into a character array structure

I am trying to read a set of values from a text file into an array of structures of arrays. The entries are each separated by a '\n', and each entry consists of 3 values, separated by a ';'.
The problem is that after correctly reading the first line of file data the program reads the first value from the second line, then seems to fail to read the remaining values. Can you point out the error in my syntax or logic?
The test data appears below.
CS162;Finish Lab 2;9/26/2009
CS201;Take Quiz 1;9/28/2009
After reading in the test data my program's output is below.
Your tasks are:
Finish Lab 2 for CS162 is due 9/26/2009
CS201
for is due
The loops that read the file into the array and outputs the array contents are below. My complete code will be at the end of the question.
for ( ; InputFile.peek() != EOF; ListSize++ )
{
InputFile.get(TaskList[ListSize].Course, BUFFERSIZE, ';');
InputFile.ignore(BUFFERSIZE, ';');
InputFile.get(TaskList[ListSize].Assignment, BUFFERSIZE, ';');
InputFile.ignore(BUFFERSIZE, ';');
InputFile.get(TaskList[ListSize].DueDate, BUFFERSIZE, ';');
InputFile.ignore(BUFFERSIZE, '\n');
}
cout << "Your tasks are:" << endl;
for ( int Iteration = 0; Iteration <= ListSize; Iteration++ )
{
cout << TaskList[Iteration].Assignment << " for " << TaskList[Iteration].Course << " is due " << TaskList[Iteration].DueDate << endl;
}
Full disclosure, this is for a computer science class. This is why I am not asking for complete code solutions, just help with logic or syntax errors. If I am doing this in completely the wrong way, please point me to documentation to help me. But this does put limitations on my code. The program must use character arrays, not strings.
Perhaps the last get should be:
InputFile.get(TaskList[ListSize].DueDate, BUFFERSIZE, '\n');
instead of
InputFile.get(TaskList[ListSize].DueDate, BUFFERSIZE, ';');
Your last field (due date) does not have a semicolon at the end, only a newline.
Update: I suggest you also look into using getline instead of get. They have similar functionality, but getline will consume the delimiter also, meaning that you won't need to use the ignore().
Without thinking about the code you have written, I'll just say that my normal pattern for this type of problem is:
while (readline) { processline; }
Incremental file processing is more likely to run into problems if you don't have everything exactly correct.