reading data from files, file name as input - c++

I am writing a program which reads data from different files, which are given as input strings, and stores them into a vector of vectors. The problem I am not able to debug the loop which reads different files. I have closed the ifstream object, cleared the string using empty function... but still it just terminates when i give second file name as input.
I am copying the code for your perusal. It is a function called by another another function. Transposectr transposes a matrix.
code:
vector<vector<float> > store1,store2;
ifstream bb;
string my_string;
float carrier;
vector<float> buffer;
cout<<"enter the file name"<<endl;
getline(cin,my_string);
while (my_string!="end")
{
bb.open(my_string.c_str());
while (!bb.eof())
{
bb >> carrier;
if (bb.peek() == '\n' || bb.eof() )
{
buffer.push_back(carrier);
store1.push_back(buffer);
buffer.clear();
}
else
{
buffer.push_back(carrier);
}
}
bb.close();
buffer.clear();
transposectr1(store1);
storex.push_back(store1[1]);
storey.push_back(store1[0]);
store1.clear();
my_string.empty();
cout<<"done reading the file"<<endl;
cout<<"enter the file name"<<endl;
getline(cin,my_string);
}

I'm really not clear what you are trying to do. But I have one golden ruile when it comes to using istreams:
Never use the eof() function!
It almost certainly does not do what you think it does. Instead you should test if a read operation succeeded.
int x;
while( in >> x ) {
// I read something successfully
}
You might also want to avoid peek() too. Try re-writing your code with this advice in mind.

Add
bb.clear();
after the bb.close() you may get the right thing. bb.close() doesn't reset the cursor I think.

Neil Butterworth is right
Never use the eof() function!
This link explains why.

Related

Why Does getline() Doesn't Read anything from a file?

I have made a code which accepts a txt file as input, and parse, and put them in 2d array myarray[][2].
Input file structure looks like this:
aaa/bbb
bbb/ccc
ccc/ddd
And it should be parsed like this:
myarray[0][0] = "aaa"
myarray[0][1] = "bbb"
myarray[1][0] = "bbb"
myarray[1][1] = "ccc"
The code which I made to do this:
void Parse_File(string file){
ifstream inFile;
inFile.open(file);
if (inFile.is_open()){
inFile.clear();
int lines = count(istreambuf_iterator<char>(inFile), istreambuf_iterator<char>(), '\n');
string myarray[lines][2];
int mycount = 0;
do{
getline(inFile, input);
myarray[mycount][0] = input.substr(0, input.find("/"));
myarray[mycount][1] = input.substr(input.find("/") +1, input.length());
mycount++;
}while (input != "");
}else{
Fatal_Err("File Doesn't Exist");
}
inFile.close();
}
But myarray doesn't have anything in it after this function. The do-while statement doesn't loop. I can't figure out why. Any help is appreciated. Thanks.
Your file had a few issues, but the major one was: You forgot to bring your file reading pointer back to the beginning of the text document. The count function took the said pointer to the end, so you needed to bring it back.
So you need to use the seekg() function to drag the pointer wherever you wish to.
See if the code below works for you
void Parse_File(string file)
{
ifstream inFile;
inFile.open(file);
if (inFile.is_open())
{
inFile.clear();
int lines = count(istreambuf_iterator<char>(inFile), istreambuf_iterator<char>(), '\n');
//Pitfall : By counting the lines, you have reached the end of the file.
inFile.seekg(0);// Pitfall solved: I have now taken the pointer back to the beginning of the file.
....
....//Rest of your code
}
}
Also, you need to learn debugging so that you understand your code more easily. I would recommend visual studio code for debugging c++.
Move "getline(inFile, input);" to the end of your loop and call it again right before you enter. input is probably "" before you enter the loop, so the loop is never called and input is never updated.

Issue reading multiple lines from .txt file in C++

I'm trying to create a student database system for a school project. I'm trying to create a function that will search a .txt file for the student id and return all of the other variables on the string. This is working great if I search for the id of the student on the first line of the txt file but isn't capturing anything if I search for a student on another line. Am I missing something obvious?
The student data is 16 strings delimited by commas on each line. The student ID is the first string.
Thanks for any assistance!
StudentType findStudent(int studentToFind)
{
ifstream inFile;
inFile.open("students.txt");
string currentLine;
string dataRead[16];
istringstream is;
int currentStudent;
if (inFile)
{
while (getline(inFile, currentLine))
{
is.str(currentLine);
for (int i = 0; i < 16; i++)
{
getline(is, dataRead[i], ',');
}
currentStudent = stoi(dataRead[0]);
if (currentStudent == studentToFind)
{
/*
Do stuff here
*/
inFile.close();
return foundStudent;
}
cin.ignore(); // Not sure if this is needed but I was trying to
// clear the \n char if that was causing the issue
}
}
}
First : you aren't using cin, so get rid of cin.ignore().
Second : you should make sure you ALWAYS close infile at the end... so I would suggest not returning early or closing early, but using a break statement to exit your loop and then have a single return of whether you found it or not.
Third: Now that you removed all the 'gorp' we can finally hone in on the problem ... effectively the question is do we read all the lines?
Well let's check that, try printing out currentLine each time at the beginning of the while loop, if you know currentLine is updated properly, is is getting updated each time? yes...
ok then look at your next loop let's print out currentStudent each time... does currentStudent print the right value for each line? i.e. is the getline write into dataRead[i] actually writing what you think it should be to the right space?
Did you find the problem yet?
This is the kind of problem you need to learn how to solve yourself using print statements and a debugger. That what its for. If you are in visual studio run in debug mode and step through it... if not, use gdb. learn it and get used to it, you'll be using it a lot!
good luck

reading until the end of file in C++

I'm trying to read till the end of a file for a phonebook app that im converting from C to C++. When I print the the results from the file i get this:
johnny smith
(Home)3
(Cell)4
x☺> x☺>
(Home)4
(Cell)4
it should print:
johnny smith
(Home)3
(Cell)4
Right now I'm using while(!infile.eof()) which i've read is a poor practice, but when I use infile.getline() I get a repeat of the first and last name, and the format is all jacked up. Is there anyway(or another way) to get rid of the junk at the end of the input or another way to read till the end of file in C++ that fixes this. I've been reading about different solutions, but the one a lot of sites seem to agree on is fgets, which is what I had with the original C version, but obviously fgets doesn't work with ifstream which is what I'm using. here is the code:
void contacts:: readfile(contacts*friends ,int* counter, int i,char buffer[],char user_entry3[])
{
ifstream read;
read.open(user_entry3,ios::in);
int len;
contacts temp;
*counter=0;
i=0;
while (!read.eof()) {
temp.First_Name=(char*)malloc(36);
temp.Last_Name=(char*)malloc(36);
read>>temp.First_Name>>temp.Last_Name;
read>>buffer;
len=strlen(buffer);
if(buffer[len-1]=='\n')
buffer[len-1]='\0';
temp.home=(char*)malloc(20);
strcpy(temp.home, buffer);
read>>buffer;
len=strlen(buffer);
if(buffer[len-1]=='\n')
buffer[len-1]='\0';
temp.cell=(char*)malloc(20);
strcpy(temp.cell, buffer);
friends[i].First_Name=(char*)malloc(MAXNAME);
friends[i].Last_Name=(char*)malloc(MAXNAME);
friends[i].home=(char*)malloc(MAXPHONE);
friends[i].cell=(char*)malloc(MAXPHONE);
//adds file content to the structure
strcpy(friends[*counter].First_Name,temp.First_Name);
strcpy(friends[*counter].Last_Name,temp.Last_Name);
strcpy(friends[*counter].home,temp.home);
strcpy(friends[*counter].cell,temp.cell);
(*counter)++;
i++;
}
//closes file and frees memory
read.close();
free(temp.Last_Name);
free(temp.First_Name);
free(temp.home);
free(temp.cell);
}
Don't use !eof(). It checks whether the last read failure was due to reaching the end of the file. It does not predict the future.
Don't use malloc in C++. If you do, check the return value for errors!
Don't use operator>> for char *. There's no size check so that's just asking for buffer overflows.
The '\n' check on buffer is useless. operator>> for strings stops at whitespace.
You're blindly strcpying a string of unknown length into temp.home of size 20. That's another buffer overflow.
... I kind of stopped reading there. If you want to read stuff from a file but stop on eof/error, you can do something like this:
.
string a, b, c;
while (true) {
if (!(in >> a)) break;
if (!(in >> b)) break;
if (!(in >> c)) break;
do_stuff_with(a, b, c);
}
Do not use eof() to determine if you reached end of file. Instead, read what you want to read and then check if you successfully read the data. Obce reading failed you may use eof() to determine if the error is down to having reached the end of the file before producing an error report about a format error.
Since you mentioned that you read that using !infile.eof() is good practice: Can you point us at the source of this wrong information? This information need correction.

For loops and inputing data?

trying to figure out how to make a little inventory program and I can't for the life figure out why it isn't working.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct record
{
int item_id;
string item_type;
int item_price;
int num_stock;
string item_title;
string item_author;
int year_published;
};
void read_all_records(record records[]);
const int max_array = 100;
int main()
{
record records[max_array];
read_all_records(records);
cout << records[2].item_author;
return 0;
}
void read_all_records(record records[])
{
ifstream invfile;
invfile.open("inventory.dat");
int slot = 0;
for (int count = 0; count<max_array; count++);
{
invfile >> records[slot].item_id >> records[slot].item_type >> records[slot].item_price >> records[slot].num_stock >> records[slot].item_title >> records[slot].item_author >> records[slot].year_published;
slot++;
}
invfile.close();
}
I'm testing it by having it print the second item from records author. When I run it, it doesn't show the authors name at all. The .dat file is located in just about every folder where the project is (I forgot which folder it needs to be in) so it's there.
The issue isn't that the file isn't working. It's the array not printing off anything.
my inv file is basically:
123456
book
69.99
16
title
etc
etc
and repeats for different books/cds etc all on one line, all without spaces. Should just next in.
You should check to see that the file is open.
invfile.open("inventory.dat");
if (!invfile.is_open())
throw std::runtime_error("couldn't open inventory file");
You should check to seen that your file reads are working and breaks when you hit the end of file.
invfile >> records[slot].item_id >> records[slot].item_type ...
if (invfile.bad())
throw std::runtime_error("file handling didn't work");
if (invfile.eof())
break;
You probably want to read each record at time, as it isn't clear from this code how the C++ streams are supposed to differentiate between each field.
Usually you'd expect to use std::getline, split the fields on however you delimit them, and then use something like boost::lexical_cast to do the type parsing.
If I were doing this, I think I'd structure it quite a bit differently.
First, I'd overload operator>> for a record:
std::istream &operator>>(std::istream &is, record &r) {
// code about like you had in `read_all_records` to read a single `record`
// but be sure to return the `stream` when you're done reading from it.
}
Then I'd use an std::vector<record> instead of an array -- it's much less prone to errors.
To read the data, I'd use std::istream_iterators, probably supplying them to the constructor for the vector<record>:
std::ifstream invfile("inventory.dat");
std::vector<record> records((std::istream_iterator<record>(invfile)),
std::istream_iterator<record>());
In between those (i.e., after creating the file, but before the vector) is where you'd insert your error handling, roughly on the order of what #Tom Kerr recommended -- checks for is_open(), bad(), eof(), etc., to figure out what (if anything) is going wrong in attempting to open the file.
Add a little check:
if (!invfile.is_open()) {
cout<<"file open failed";
exit(1);
}
So that way, you don't need to copy your input file everywhere like you do now ;-)
You are reading in a specific order, so your input file should have the same order and required number of inputs.
You are printing 3rd element of the struct records. So you should have at least 3 records. I don't see anything wrong with your code. It would a lot easier if you can post your sample input file.

Is there anyway to reset the filein to the initial state?

I am trying to input data from a text file in C++.
The text file is in that format:
4 15
3 516
25 52 etc.
Each line contains two integers. I don't know the number of lines in the file so I can bind enough memory and this is what I have come into as a way to solve that:
ifstream filein;
filein.open("text.txt",ios::in);
int count=0;
while (!filein.eof())
{
count++;
filein>>temporary;
}
count=count/2; // This is the number of lines in the text file.
My problem is that I can't figure out a way to reset
filein
into the initial state (to the beggining of the file so I can actually input the data) other than closing the input stream and opening it again. Is there any other way to do that?
Rather than answer the question you asked, I'm going to answer the question you didn't ask, namely:
Q: How can I read in all the lines of the file if I don't know how many lines there are?
A: Use a std::vector<>.
If you want to read in all of the numbers, regardless of pairing:
// all code fragments untested. typos are possible
int i;
std::vector<int> all_of_the_values;
while(filein >> i)
all_of_the_values.push_back(i);
If you want to read in all of the numbers, putting alternating numbers into different data structures:
int i, j;
std::vector<int> first_values;
std::vector<int> second_values;
while(filein >> i >> j) {
first_values.push_back(i);
second_values.push_back(j);
If you want to read in all of the numbers, storing them in some sort of data structure:
int i, j;
struct S {int i; int j;};
std::vector<S> values;
while(filein >> i >> j) {
S s = {i, j};
values.push_back(s);
}
Finally, if you want to read the file a line at a time, keeping the first two numbers from each line, discarding the remainder of each line, and storing them a user-defined data structure:
std::vector<MyClass> v;
std::string sline;
while(std::getline(filein, sline)) {
std::istringstream isline(sline);
int i, j;
if(isline >> i >> j) {
values.push_back(MyClass(i, j));
}
}
Aside: never use eof() or good() in a loop conditional. Doing so almost always produces buggy code, as it would have in your case. Instead prefer invoking the input function in the condition, as I have done above.
I think #Robᵩ has pretty much the right idea -- instead of reading through all the data just to count the number of lines, then reading through the whole file again to actually read the data, using something like std::vector (or std::deque) that will expand as needed as you read the data.
In a typical case, however, the two numbers on a line are going to be related to each other, and you typically want to store them in a way that shows that association directly. For example, they might be the X and Y coordinates of points, in which case you want to read points:
class point {
int x, y;
};
std::istream &operator>>(std::istream &is, point &p) {
return is >> p.x >> p.y;
}
std::ifstream in("myfile.txt");
// create the vector from the data in the file:
std::vector<point> points((std::istream_iterator<point>(in)),
std::istream_iterator<point>());
On a slightly different note: even if you decide you want to use an explicit loop, please don't use while (!whatever.eof()) to do it -- that's pretty much guaranteed to fail. You want to check that reading data succeeded, so (for example) using the point class above, you could use something like:
point p;
while (infile >> p)
points.push_back(p);
The function is: filein.seekg (0, ios::beg);
Here is a Reference
You should also use filein.clear() to reset the eof bit in the file if you do it this way.
And, of course, if you want the best method for what you are ultimately trying to do, Robᵩ's answer is much better, albeit more involved.