ifstream does not read first line - c++

I am using the code with ifstream that I used ~1 year ago, but now it does not work correctly. Here, I have the following file (so, just a line of integers):
2 4 2 3
I read it while constructing a graph from this file:
graph g = graph("file.txt");
where graph constructor starts with:
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
graph::graph(const char *file_name) {
ifstream infile(file_name);
string line;
getline(infile, line);
cout << line << endl; // first output
istringstream iss;
iss.str(line);
iss >> R >> C >> P >> K;
iss.clear();
cout << R << " " << C << " " << P << " " << K; // second output
}
The second output (marked in code), instead of giving me 2 4 2 3, returns random(?) values -1003857504 32689 0 0. If I add the first output to check the contents of line after getline, it is just an empty string "".
All the files (main.cpp where a graph is instantiated, 'graph.cpp' where the graph is implemented and 'file.txt') are located in the same folder.
As I mentioned, this is my old code that worked before, so probably I do not see some obvious mistake which broke it. Thanks for any help.

These two locations:
where your program's original source code is located
where your program's input data is located
are completely unrelated.
Since "file.txt" is a relative path, your program looks for input data in the current working directory during execution. Sometimes that is the same as where the executable is. Sometimes it is not. (Only you can tell what it is, since it depends on how you execute your program.) There is never a connection to the location of the original source file, except possibly by chance.
When the two do not match, you get this problem, because you perform no I/O error checking in your program.
If you checked whether infile is open, I bet you'll find that it is not.
This is particularly evident since the program stopped working after a period of time without any changes to its logic; chances are, the only thing that could have changed is the location of various elements of your solution.

Related

Filling a cstring using <cstring> with text from a textfile using File I/O C++

I began learning strings yesterday and wanted to manipulate it around by filling it with a text from a text file. However, upon filling it the cstring array only prints out the last word of the text file. I am a complete beginner, so I hope you can keep this beginner friendly. The lines I want to print from the file are:
"Hello World from UAE" - First line
"I like to program" - Second line
Now I did look around and eventually found a way and that is to use std::skipary or something like that but that did not print it the way I had envisioned, it prints letter by letter and skips each line in doing so.
here is my code:
#include <fstream>
#include <iostream>
#include <cstring>
#include <cctype>
using namespace std;
int main() {
ifstream myfile;
myfile.open("output.txt");
int vowels = 0, spaces = 0, upper = 0, lower = 0;
//check for error
if (myfile.fail()) {
cout << "Error opening file: ";
exit(1);
}
char statement[100];
while (!myfile.eof()) {
myfile >> statement;
}
for (int i = 0; i < 30; ++i) {
cout << statement << " ";
}
I'm not exactly sure what you try to do with output.txt's contents, but a clean way to read through a file's contents using C++ Strings goes like this:
if (std::ifstream in("output.txt"); in.good()) {
for (std::string line; std::getline(in, line); ) {
// do something with line
std::cout << line << '\n';
}
}
You wouldn't want to use char[] for that, in fact raw char arrays are hardly ever useful in modern C++.
Also - As you can see, it's much more concise to check if the stream is good than checking for std::ifstream::fail() and std::ifstream::eof(). Be optimistic! :)
Whenever you encounter output issues - either wrong or no output, the best practise is to add print (cout) statements wherever data change is occurring.
So I first modified your code as follows:
while (!myfile.eof()) {
myfile >> statement;
std::cout<<statement;
}
This way, the output I got was - all lines are printed but the last line gets printed twice.
So,
We understood that data is being read correctly and stored in statement.
This raises 2 questions. One is your question, other is why last line is printed twice.
To answer your question exactly, in every loop iteration, you're reading the text completely into statement. You're overwriting existing value. So whatever value you read last is only stored.
Once you fix that, you might come across the second question. It's very common and I myself came across that issue long back. So I'm gonna answer that as well.
Let's say your file has 3 lines:
line1
line2
line3
Initially your file control (pointer) is at the beginning, exactly where line 1 starts. After iterations when it comes to line3, we know it's last line as we input the data. But the loop control doesn't know that. For all it knows, there could be a million more lines. Only after it enters the loop condition THE NEXT TIME will it come to know that the file has ended. So the final value will be printed twice.

searching a name in the csv file on C++

I am a young programmer who is trying to learn c++. i have a working csv.file. but i want to search for a specific number assigned to the name and then displays the name of what i'm looking for. i have the file here:
1,Bulbasaur,grass
2,Ivysaur, grass
3,Venusaur, grass
4,Charmander, fire
5,Charmeleon, fire
6,Charizard, fire
7,Squirtle, water
8,Wartortle, water
9,Blastoise, water
Code
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream ip("pokedex.csv");
string pokedexnum[9];
string pokemonName[9];
string pokemonType[9];
cout<<"please enter a pokemon number:"<<" ";
cin>>pokemonType[0];
while (ip.good()){
getline( ip, pokedexnum[0]);
getline( ip, pokemonName[0]);
getline( ip, pokemonType[0]);
}
cout<<"the pokemon that is:"<< " "<<pokedexnum[0]<< "is the pokemon called:"<< pokemonName[0];
ifstream close("pokedex.csv");
return 0;
}
when it runs
please enter a pokemon number: 1
the pokemon that is: is the pokemon called:8,Wartortle, water
could you please point out what i am doing wrong?
Among the issues in this code:
You're not using std::getline correctly for comma-separated data. The result is each pass is consuming three lines from your input file; not three values from each line.
You're also not using ip.good() correctly as a while-condition.
You're retaining your test value in the array, which will be overwritten on the first iteration pass, so it is lost.
You're ignoring potential IO failures with each std::getline invoke.
You're overwriting slot-0 in your arrays with each loop iteration.
Minor, ifstream close("pokedex.csv"); clearly isn't doing what you think it is. That just creates another fstream object called close on the given file name.
The later may be intentional for now, but clearly broken in the near future.
In reality, you don't need arrays for any of this. All you're doing is reading lines, and seem to want to test the input number against that of the CSV data first column, reporting the line that you find, then ending this.
So do that:
Read the input value to search for.
Open the file for scanning.
Enumerate the file one line at a time.
For each line from (3), use a string stream to break the line into the comma separated values.
Test the id value against the input from (1). If the same, report the result and break the loop; you're done.
The result is something like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>
int main()
{
std::cout<<"please enter a pokemon number: ";
long num;
if (std::cin >> num && num > 0)
{
std::ifstream ip("pokedex.csv");
std::string line;
while (std::getline(ip, line))
{
std::istringstream iss(line);
std::string id, name, skill;
if (std::getline(iss, id, ',') &&
std::getline(iss, name, ',') &&
std::getline(iss, skill))
{
char *endp = nullptr;
long n = std::strtol(id.c_str(), &endp, 10);
if (id.c_str() != endp && n == num)
{
std::cout << "The pokemon that is: " << num << " is called: " << name << '\n';
break;
}
}
}
}
}
Admittedly untested, but it should work.
Whether you want to store the items in arrays at this point is entirely up to you, but it isn't needed to solve the somewhat abstract problem you seem to be attempting, namely finding the matching line and reporting the name from said-same. If you still want to store them in arrays, I suggest you craft a structure to do so, something like:
struct Pokemon
{
int id;
std::string name;
std::string skill;
};
and have a single array of those, rather than three arbitrary arrays that must be kept in sync.
Four issues jump out at me:
You store the user's input into pokemonType, but then also use pokemonType for reading data from your CSV file. The file input is going to overwrite the user input.
Your file input loop always references index 0. All of the lines from your data file are going into element 0. That's the main reason that even if the user inputs 1, the output is from the last line of the data file.
Your file reading loop is structured like you want to put one part of each data line into a different array, but what you've written actually reads three lines on every iteration, storing those lines into the three different arrays.
This isn't affecting your output, but the code ifstream close("pokedex.csv"); is written like you want to close the file stream you opened, but I do believe what this line actually does is create a new ifstream called close, and opens pokedex.csv attached to it. In other words, it's just like your other line ifstream ip("pokedex.csv"); but with close as the variable name instead of ip.
You are going to want to look into something called "string tokenization". Start with some web searches, apply what you read about to your code, and of course if you hit another snag, post a new question here to Stack Overflow, showing (as you did here) what you tried and in what way it isn't working.
Elaborating on #3, here's what how your data file is being read:
at the end of the 1st iteration of the file-reading loop, ...
pokedexnum[0] is "1,Bulbasaur,grass"
pokemonName[0] is "2,Ivysaur, grass"
pokemonType[0] is "3,Venusaur, grass"
at the end of the 2nd iteration of the file-reading loop, ...
pokedexnum[0] is "4,Charmander, fire"
pokemonName[0] is "5,Charmeleon, fire"
pokemonType[0] is "6,Charizard, fire"
at the end of the 3rd iteration of the file-reading loop, ...
pokedexnum[0] is "7,Squirtle, water"
pokemonName[0] is "8,Wartortle, water"
pokemonType[0] is "9,Blastoise, water"
And that's why
<< "is the pokemon called:"<< pokemonName[0];
outputs
is the pokemon called:8,Wartortle, water

C++ copying parts of a file to a new file

I am trying to create a new file with data from two different existing files. I need to copy the first existing file in it's entirety, which I have done successfully. For the second existing file I need to copy just the last two columns and append it to the first file at the end of each row.
Ex:
Info from first file already copied into my new file:
20424297 1092 CSCI 13500 B 3
20424297 1092 CSCI 13600 A- 3.7
Now I need to copy the last two columns of each line in this file and then append them to the appropriate row in the file above:
17 250 3.00 RNL
17 381 3.00 RLA
i.e. I need "3.00" and "RNL" appended to the end of the first row, "3.0" and "RLA" appended to the end of the second row, etc.
This is what I have so far:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
using namespace std;
int main() {
//Creates new file and StudentData.tsv
ofstream myFile;
ifstream studentData;
ifstream hunterCourseData;
//StudentData.tsv is opened and checked to make sure it didn't fail
studentData.open("StudentData.tsv");
if(studentData.fail()){
cout << "Student data file failed to open" << endl;
exit(1);
}
//My new file is opened and checked to make sure it didn't fail
myFile.open("file.txt");
if(myFile.fail()){
cout << "MyFile file failed to open" << endl;
exit(1);
}
//HunterCourse file is opened and checked to make sure if didn't fail
hunterCourseData.open("HunterCourse.tsv");
if(myFile.fail()){
cout << "Hunter data file failed to open" << endl;
exit(1);
}
// Copies data from StudentData.tsv to myFile
char next = '\0';
int n = 1;
while(! studentData.eof()){
myFile << next;
if(next == '\n'){
n++;
myFile << n << ' ';
}
studentData.get(next);
}
return 0;
}
I am going bananas trying to figure this out. I'm sure it's a simple fix but I can't find anything online that works. I've looked into using ostream and a while loop to assign each row into a variable but I can't get that to work.
Another approach that has crossed my mind is just to remove all integers from the second file because I only need the last two columns and neither of those columns include integers.
If you take a look at the seekg method of a file-stream, you'll note the second version allows you to implement the location to set an offset from (such as ios_base::end which sets the offset compared to the end of the file. With this you can effectively read backwards from the end of the a file.
Consider the following
int Pos=0;
while(hunterCourseData.peek()!= '\n')
{
Pos--;
hunterCourseData.seekg(Pos, ios_base::end);
}
//this line will execute when you have found the first newline-character from the end of the file.
Much better code is available at this Very Similar question
Another possibility is simply to find how many lines are in the file beforehand. (less fast, but workable), in this case one would simply loop though the file calling getline and increment a count variable, reset to the start, then repeat until reaching the count - 2. Though I wouldn't use this technique myself.

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.

Can't get ios::beg to go back to the beginning of the file

It always seems to be the things that should be no problem that cause problems for me. I don't get it. :/
So I'm trying to make sure that I understand how to manipulate text files. I've got two files, "infile.txt" and "outfile.txt". "infile.txt" has six numbers in it and nothing else. Here is the code I used to manipulate the files.
#include<fstream>
using std::ifstream;
using std::ofstream;
using std::fstream;
using std::endl;
using std::ios;
int main()
{
ifstream inStream;
ofstream outStream;//create streams
inStream.open("infile.txt", ios::in | ios::out);
outStream.open("outfile.txt");//attach files
int first, second, third;
inStream >> first >> second >> third;
outStream << "The sum of the first 3 nums is " << (first+second+third) << endl;
//make two operations on the 6 numbers
inStream >> first >> second >> third;
outStream << "The sum of the second 3 nums is " << (first+second+third) << endl;
inStream.seekg(0); //4 different ways to force the program to go back to the beginning of the file
//2. inStream.seekg(0, ios::beg);
//3. inStream.seekg(0, inStream.beg);
//4. inStream.close(); inStream.open("infile.txt");
//I have tried all four of these lines and only #4 works.
//There has got to be a more natural option than just
//closing and reopening the file. Right?
inStream >> first >> second >> third;
outStream << "And again, the sum of the first 3 nums is " << (first+second+third) << endl;
inStream.close();
outStream.close();
return 0;
}
Maybe I don't understand quite how the stream works, but I've seen a few sources that said that seekg(0) should move the index back to the start of the file. Instead, this is what I get out of it.
The sum of the first 3 nums is 8
The sum of the second 3 nums is 14
And again, the sum of the first 3 nums is 14
It went back, but not nearly in the way I would have hoped. Any idea why this happened? Why did my first three attempts fail?
As Bo Persson states, it may be because your input has
encountered end of file; it shouldn't, because in C++, a text
file is defined as being terminated by a '\n', but practically
speaking, if you're working under Windows, a lot of ways of
generating a file will omit this final '\n'—although it
is formally required, practical considerations will mean that
you'll make sure that it works even if the final '\n' is
missing. And I can't think of any other reason off hand why the
seekg's wouldn't work. inStream.seekg( 0 ) is, of course,
undefined behavior, but in practice, it will work pretty much
everywhere. inStream.seekg( 0, ios::beg ) is guaranteed to
work if inStream.good(), and is, IMHO, preferable to the
first form. (The single argument form of seekg is normally
only used with the results of a tellg as an argument.) And of
course, it only works if the actual input source supports
seeking: it won't work if you're reading from a keyboard or
a pipe (but presumably, "infile.txt" is neither).
In general, you should check the status of inStream after each
read, before using the results. But if the only problem is that
the file doesn't end with '\n', it's probable that the status
will be OK (!fail()) after the final read, even if you've
encountered end of file. In which case, you'll need clear()
anyway.
Note that the above comments are valid for C++-03 and precedent.
C++11 has changed the specification of the single argument form
of seekg, and requires it to reset eofbit before anything
else. (Why is this change only for the single argument form of
seekg, and not the two argument form? Oversight?)
The second input reaches end-of-file for the stream. That state sticks until you call inStream.clear() to clear its state (in addition to the seek).
With a C++11 compliant compiler, option 4 should also work as close and reopen will now clear the previous state. Older compilers might not do that.
Try:
inStream.seekg(0, ios_base::beg);