std::getline not producing correct output (c++) - c++

I'm doing a homework assignment in c++ and I could use a little help. I am not understanding why the following code isn't working as I want it. The object of the function I'm creating is to load a file and parse it into keys and values for a map while skipping blank lines and lines where the first character is a hastag. The file I'm reading from is below.
The problem is that my nextToken variable is not being delimited by the '=' character. I mean, when I cout nextToken, it doesn't equal the string before the '=' character. For example, the first two lines of the data file are
# Sample configuration/initialization file
DetailedLog=1
I thought that the code I have should skip all the lines that begin with a hashtag (but it's only skipping the first line) and that nextToken would equal just DetailedLog (as opposed to DetailedLog=1 or just equal to 1).
In my output, some lines with a hashtag are skipped while some are not and I can't understand where cout is printing from since the cout statement I have should print "nextToken: " and then nextToken, but it's printing nextToken then "nextToken: " then what comes after the '=' character from the data file.
Here's my code:
bool loadFile (string filename){
ifstream forIceCream(filename);
string nextToken;
if (forIceCream.is_open()){
while (getline(forIceCream, nextToken, '=')) {
if (nextToken.empty() || nextToken[0] == '#') {
continue;
}
cout << "nextToken: " << nextToken << endl;
}
}
}
Data file reading from:
# Sample configuration/initialization file
DetailedLog=1
RunStatus=1
StatusPort=6090
StatusRefresh=10
Archive=1
LogFile=/tmp/logfile.txt
Version=0.1
ServerName=Unknown
FileServer=0
# IP addresses
PrimaryIP=192.168.0.13
SecondaryIP=192.168.0.10
# Random comment

If the first two lines of your input file are:
# Sample configuration/initialization file
DetailedLog=1
Then, the call
getline(forIceCream, nextToken, '=')
will read everything up to the first = to nextToken. At the end of the line, the value of nextToken will be:
# Sample configuration/initialization file
DetailedLog
See the documentation of std::getline and pay attention to the first overload.
You need to change your strategy for processing the contents of the file a little bit.
Read the contents of the file line by line.
Process each line as you see fit.
Here's an updated version of your function.
bool loadFile (string filename)
{
ifstream forIceCream(filename);
if (forIceCream.is_open())
{
// Read the file line by line.
string line;
while ( getline(forIceCream, line) )
{
// Discard empty lines and lines starting with #.
if (line.empty() || line[0] == '#')
{
continue;
}
// Now process the line using a istringstream.
std::istringstream str(line);
string nextToken;
if ( getline(str, nextToken, '=') )
{
cout << "nextToken: " << nextToken << endl;
}
}
}
}

Related

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 print X amount of lines after finding a string in a file using C++

I'm trying to implement a function in C++ that searches for a string in a file, then prints the the line containing the string AND X subsequent lines.
I have the following code that works for finding the string and printing the line, but I can't get it to work to print the lines under the line with the string.
void repturno(void){
system("cls");
string codemp, line,output;
bool found = false;
ifstream myfile ("Pacientes.csv");
//captures the string that i need to look for, in this case an employee code
cout<<"\nBienvenida enfermera en turno, por favor introduzca su codigo"<<endl;
cin.ignore();
getline(cin,codemp);
system("cls");
/*reads the file and searches for the string, then prints the whole line, then searches again for
the string and if finds it, will print the whole line again*/
while (getline(myfile, line)) {
if (line.find(codemp) != string::npos) {
cout<<line<<endl;
getline(myfile,line);
found = true;
}
}
//using this to check if the code was working, and verifying if the string was found or not :P
if( found == false){
cout <<"No se encontro la cadena"<< endl;
}
system("pause");
return menu();
}
Here are my problems:
I can't get the cursor to go down to the next line and print it (need to do this 4 times), everything just gets messy and I end up with the same string and its line printed all over the screen many times.
I can't save all the data pertinent to each employee in a single line (it would solve the problem in this function, but would create other problems); there's too much info for each employee, way more than the 80 characters allowed in the console and it messes up other parts of my program where I have to display the information on the screen and it looks really bad when i print it, even when trying to use gotoxy or '\t' for proper spacing. I had to save the data line by line, each category in a different line, so I can properly display it in other parts of my programs.
I came with 2 solutions in pseudo-code, but i don't know how to translate them into C++ sentences:
NOTE: the search loop is already defined in the code above, so won't break it down step by step in the pseudo-code, will refer to it as SEARCH LOOP
Pseudo-code #1
START SEARCH LOOP
SEARCH for the desired string(`codemp`) line by line
IF string is found
PRINTLINE containing the STRING
MOVE CURSOR one line below
PRINT the entire line
MOVE CURSOR one line below
PRINT the entire line
MOVE CURSOR one line below
PRINT the entire line
MOVE CURSOR one line below
PRINT the entire line
Move CURSOR one line below
RESTART SEARCH LOOP in the current line
IF NO matching string is found AND EOF
END IF
END SEARCH LOOP
END PROGRAM
Psuedo-code #2
START SEARCH LOOP
SEARCH for the desired string(codemp) line by line
IF string is found
PRINTLINE containing the string
PRINTING LOOP
MOVE CURSOR one line below
ASSIGN line to a temp_string
IF temp_string = "\n\n"
//each employee is separated by 2 whitelines after its info
DO NOTHING
ELSE
PRINTLINE temp_string
RESTART PRINTING LOOP
END IF
END PRINTING LOOP
RESTART SEARCH LOOP
IF NO string is found AND EOF
END SEARCH LOOP
END PROGRAM
I hope this is clear and detailed enough. That's what I want to do with my program, but I can't find a way to translate it into c++ sentences. Please let me know if there's a better, or more efficient way to do what I want to do.
I didn't test it but that should work :
while( getline(myfile,line) )
{
if( line.find( codemp ) != string::npos )
{
cout << line << endl;
for( int i = 0; i < nbLines && getline( myfile, line ); ++i )
cout << line << endl;
}
}
It iterate the file with a while and when it find the string it print "nbLines" other lines"
Since what you have is working for you, let's look at that first. Here I've xtracted just the relevant portion, with a small addition. All we need to do is print a four more lines in the case that the code was found, so:
while (getline(myfile, line)) {
if (line.find(codemp) != string::npos) {
cout<<line<<endl;
getline(myfile,line);
found = true;
for (int i=4; i; --i) { <== new code begins
cout << line << '\n';
getline(myfile, line);
} <== new code ends
}
}
Simple as that.

Deleting from a certain point in a file to the end of the line?

I'm having some trouble with detecting two '//' as a char and then deleting from the first '/' till the end of the line (im guessing /n comes into use here).
{
ifstream infile;
char comment = '//';
infile.open("test3.cpp");
if (!infile)
{
cout << "Can't open input file\n";
exit(1);
}
char line;
while (!infile.eof())
{
infile.get(line);
if (line == comment)
{
cout << "found it" << endl;
}
}
return 0;
}
In the test3.cpp file there are three comments, so 3 lots of '//'. But I can't detect the double slash and can only detect a single / which will affect other parts of the c++ file as I only want to delete from the beginning of a comment to the end of the line?
I'm having some trouble with detecting two '//' as a char
That's because // is not a character. It is a sequence of two characters. A sequence of characters is known as a string. You can make string literals with double quotation marks: "//".
A simple solution is to compare the current input character from the stream to the first character of the string "//" which is '/'. If it matches, then compare the next character from the stream with the second character in the string that is searched for. If you find two '/' in a row, you have your match. Or you could be smart and read the entire line into a std::string and use the member functions to find it.
Also:
while (!infile.eof())
{
infile.get(line);
// using line without testing eof- and badbit
This piece of code is wrong. You test for eofbit before reading the stream and process the input.
And your choice of name for the line variable is a bit confusing since it doesn't contain the entire. line but just one character.

Why does getline return empty lines if none exist

I have a file containing the following lines:
5556
0 bla.dxf
1 blub.dxf
2 buzz.dxf
The numbers and text are seperated by a singular tab each, there is no whitespace character after 5556. The following code is used for parsing.
int main(int, char**){
std::ifstream file("test.bld");
std::string buildingName;
file >> buildingName;
std::cout << buildingName << std::endl;
std::string buf;
while(getline(file, buf)) {
if(buf.empty()){std::cout << "String was empty"<<std::endl;}
else std::cout << buf << std::endl;
}
return 0;
}
When I parse the file I get an empty line although there obviously is none.
The output reads as follows:
5556
String was empty
0 bla.dxf
1 blub.dxf
2 buzz.dxf
This is only a minimal example. The whole file and the parser is more complex and I would very much like to use direct parsing for the first element and getline for the rest. What am I misunderstanding about line parsing with getline and how do I avoid getting empty lines?
operator>>(istream, string) reads up to but not including the first whitespace character after the extracted token.
To skip the rest of the line after extracting a token, you can either use
std::cin >> std::ws;
(if you know that there is only a newline remaining), or
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
to skip to the end of the line regardless.
I'm assuming because
file >> buildingName;
doesn't move the cursor to the next line, but leaves it at the end of the current line. So when you call getline, you'll read an empty string and then move to the next.

trying to read a text file data into an array to be manipulated then spit back out

My aim is to take the data from the file, split it up and place them into an array for future modification.
The is what the data looks like:
course1-Maths|course1-3215|number-3|professor-Mark
sam|scott|12|H|3.4|1/11/1991|3/15/2012
john|rummer|12|A|3|1/11/1982|7/15/2004
sammy|brown|12|C|2.4|1/11/1991|4/12/2006
end_Roster1|
I want to take maths, 3215, 3 and Mark and put into an array,
then sam scott 12 H 3.4 1/11/1991 3/15/2012.
This is what I have so far:
infile.open("file.txt", fstream::in | fstream::out | fstream::app);
while(!infile.eof())
{
while ( getline(infile, line, '-') )
{
if ( getline(infile, line, '|') )
{
r = new data;
r->setRcourse_name(line);
r->setRcourse_code(3);//error not a string
r->setRcredit(3);//error not a string pre filled
r->setRinstructor(line);
cout << line << endl;
}
}
}
Then I tried to view it nothing is stored.
Firstly line 1 is very different to the remaining lines so you need a different parsing algorithm for them. Something like:
bool first = true;
while(!infile.eof())
{
if (first)
{
// read header line
first = false;
}
else
{
// read lines 2..n
}
}
Reading lines 2..n can be handled by making a stringstream for each line, then passing that in to another getline using '|' as a delimeter, to get each token (sam, scott, 12, H, 3.4, 1/11/1991, 3/15/2012)
if (getline(infile, line, '\n'))
{
stringstream ssline(line);
string token;
while (getline(ssline, token, '|'))
vector.push_back(token);
}
Reading the header line takes the exact same concept one step further where each token is then further parsed with another getline with '-' as a delimiter. You'll ignore each time the first tokens (course1, course1, number, professor) and use the second tokens (Maths, 3215, 3, Mark).
You are completely ignoring the line that you get inside the condition of the nested while loop. You should call getline from a single spot in your while loop, and then examine its content using a sequence of if-then-else conditions.