I'm working on code for one of my programming classes and my professor won't allow any of the code to be changed besides that within each function (function arguments and the main function are not allowed to be changed). The issue comes with one of the functions making use of ifstream&.
The issue arises in this specific function, which is supposed to take in the all of the data from the input file (in this case programmers.txt), but the program doesn't read the data from the file at all.
int readProgrammers(ifstream& inputFile, Programmer programmers[], int maxProgrammers )
{
inputFile.open("programmers.txt", std::ios_base::in);
for (int i = 0; i < maxProgrammers; i++)
{
inputFile >> programmers[i].programmer_id;
inputFile >> programmers[i].name;
inputFile >> programmers[i].lines;
}
return 0;
}
I've messed around with .ignore() as well as quadruple checking whether or not I got the name of the file wrong (which I didn't). I also tried going without using .open inside of the function to see if that was messing around with this, but I ended up with the same exact result. I'm honestly not sure as to what the issue is since I rarely use ifstream& in functions as it usually turns out extremely buggy.
For a little more info so as to help, this is the data within the file that's being read.
1 Alvin 300
2 Brenda 350
3 Chris 250
4 Dana 500
5 Eli 450
6 Faye 320
7 Glen 670
8 Hannah 230
9 Ian 420
10 Jade 380
I'm supposed to try and store this information into an array Programmer structs. What I'm doing wrong that's causing the file not to be read?
Edit:
I got rid of the .open within the function and checked with getLine and cout to see if the file is being read, and it is by getLine, so I'm not sure why the data isn't being stored/read by this code:
for (int i = 0; i < maxProgrammers; i++)
{
inputFile >> programmers[i].programmer_id;
inputFile >> programmers[i].name;
inputFile >> programmers[i].lines;
}
Don't call open() on a std::ifstream that is passed in to the function. It should be the caller's responsibility to make sure the stream is open before calling the function. Just read from whatever open stream is given to you.
I suggest you use std::getline() to read the input stream a line at a time, using a std::istringstream to read individual values from each line.
Try this:
int readProgrammers(ifstream& inputFile, Programmer programmers[], int maxProgrammers)
{
int count = 0;
for (int i = 0; i < maxProgrammers; i++)
{
string line;
if (!getline(inputFile, line)) break;
istringstream iss(line);
iss >> programmers[i].programmer_id;
iss >> programmers[i].name;
iss >> programmers[i].lines;
++count;
}
return count;
}
ifstream inputFile("programmers.txt");
Programmer programmers[10];
readProgrammers(inputFile, programmers, 10);
Related
This question already has answers here:
Why does reading a record struct fields from std::istream fail, and how can I fix it?
(9 answers)
Closed 6 years ago.
I was wondering how to store data from a CSV file into a structured array. I realize I need to use getline and such and so far I have come up with this code:
This is my struct:
struct csvData //creating a structure
{
string username; //creating a vector of strings called username
float gpa; //creating a vector of floats called gpa
int age; //creating a vector of ints called age
};
This is my data reader and the part that stores the data:
csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (infile.is_open()) //error check
int i=0;
while(getline(infile, data));
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
Further, this is how I was attempting to print out the information:
for (int z = 0; z<10; z++)
{
cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
}
However, when running this command, I get a cout of what seem to be random numbers:
1.83751e-0383 03 4.2039e-0453 1.8368e-0383 07011688
I assume this has to be the array running not storing the variables correctly and thus I am reading out random memory slots, however, I am unsure.
Lastly, here is the CSV file I am attempting to read.
username,gpa,age
Steven,3.2,20
Will,3.4,19
Ryan,3.6,19
Tom,3,19
There's nothing in your parsing code that actually attempts to parse the single line into the individual fields:
while(getline(infile, data));
{
This correctly reads a single line from the input file into the data string.
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
You need to try to explain to your rubber duck how this is supposed to take a single line of comma-separated values, like the one you showed in your question:
Steven,3.2,20
and separate that string into the individual values, by commas. There's nothing about the >> operator that will do this. operator>> separates input using whitespaces, not commas. Your suspicions were correct, you were not parsing the input correctly.
This is a task that you have to do yourself. I am presuming that you would like, as a learning experience, or as a homework assignment, to do this yourself, manually. Well, then, do it yourself. You have the a single line in data. Use any number of tools that C++ gives you: the std::string's find() method, or std::find() from <algorithm>, to find each comma in the data string, then extract each individual portion of the string that's between each comma. Then, you still need to convert the two numeric fields into the appropriate datatypes. And that's when you put each one of them into a std::istringstream, and use operator>> to convert them to numeric types.
But, having said all that, there's an alternative dirty trick, to solve this problem quickly. Recall that the original line in data contains
Steven,3.2,20
All you have to do is replace the commas with spaces, turning it into:
Steven 3.2 20
Replacing commas with spaces is trivial with std::replace(), or with a small loop. Then, you can stuff the result into a std::istringstream, and use operator>> to extract the individual whitespace-delimited values into the discrete variables, using the code that you've already written.
Just a small word of warning: if this was indeed your homework assignment, to write code to manually parse and extract comma-delimited values, it's not guaranteed that your instructor will give you the full grade for taking the dirty-trick approach...
UNDER CONSTRUCTION
Ton, nice try and nice complete question. Here is the answer:
1) You have a semicolon after the loop:
while(getline(infile, data));
delete it.
How did I figure that out easily? I compiled with all the warnings enabled, like this:
C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
main.cpp:24:33: warning: while loop has empty body [-Wempty-body]
while(getline(infile, data));
^
main.cpp:24:33: note: put the semicolon on a separate line to silence this warning
1 warning generated.
In fact, you should get that warning without -Wall as well, but get into using it, it will also make good to you! :)
2) Then, you read some elements, but not 10, so why do you print 10? Print as many as the ones you actually read, i.e. i.
When you try to print all 10 elements of your array, you print elements that are not initialized, since you didn't initialize your array of structs.
Moreover, the number of lines in datafile.csv was less than 10. So you started populating your array, but you stopped, when the file didn't have more lines. As a result, some of the elements of your array (the last 6 elements) remained uninitialized.
Printing uninitialized data, causes Undefined Behavior, that's why you see garbage values.
3) Also this:
if (infile.is_open()) //error check
could be written like this:
if (!infile.is_open())
cerr << "Error Message by Mr. Tom\n";
Putting them all together:
WILL STILL NOT WORK, BECAUSE ss >> arrayData[i].username; eats the entire input line and the next two extractions fail, as Pete Becker said, but I leave it here, so that others won't make the same attempt!!!!!!!
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
struct csvData //creating a structure
{
string username; //creating a vector of strings called username
float gpa; //creating a vector of floats called gpa
int age; //creating a vector of ints called age
};
int main()
{
csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (!infile.is_open()) { cerr << "File is not opened..\n"; }
int i=0;
while(getline(infile, data))
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
for (int z = 0; z< i; z++)
{
cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
}
return 0;
}
Output:
C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
username,gpa,age00
Steven,3.2,2000
Will,3.4,1900
Ryan,3.6,1900
Tom,3,1900
But wait a minute, so now it works, but why this:
while(getline(infile, data));
{
...
}
didn't?
Because, putting a semicolon after a loop is equivalent to this:
while()
{
;
}
because as you probably already know loops with only one line as a body do not require curly brackets.
And what happened to what I thought it was the body of the loop (i.e. the part were you use std::stringstream)?
It got executed! But only once!.
You see, a pair of curly brackets alone means something, it's an anonymous scope/block.
So this:
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
functioned on its one, without being part of the while loop, as you intended too!
Any why did it work?! Because you had declared i before the loop! ;)
int i = 5;
char c = 'n';
string st = "cool";
ofstream my("we.txt");
my <<st<<","<<c<<","<<i<<","<<endl;
string s;
ifstream your("we.txt");
while(your){
getline(your, st, ',');
your>>c;
your.ignore(1);
your>>i;
your.ignore(1);
cout<<st<<","<<c<<","<<i<<endl;
}
my.close();
I don't even know what your.ignore(1) stands for, but somehow it works. The problem is that it print the result twice, why?
Can someone explain me how to handle the info that I've written in the file? If I wanted to save in a file 3 info string trade, double price and char sold (y/n), about every item I have how can I manage it?
It would look like this
Timberland, 40, n;
Gucci, 10, y; ..... etc..
I need it now, cause tomorrow I have a test, I appreciate your help, and sorry for my english!
As for the problem with the double-printing, it's because the eofbit flag is not set until after you read from beyond the end of the file. That means your loop will iterate once to many.
I suggest you instead use getline to read the complete line (in the condition of the loop), and then use e.g. std::istringstream to parse the line.
So do e.g.
while (std::getline(your, fullLine))
{
std::istringstream istr(fullLine);
getline(istr, st, ',');
istr >> c;
// etc.
}
I currently have a text file that is as follows:
12 6 4 9
It is a very simple text file since I want to just get one line working and then maybe expand to multiple lines later. Extra aside: this is for a RPN calculator I am working on.
I want to go through this text file character by character. The way I currently have it implemented is with a simple while loop:
string line;
while (!infile.eof()){
getline(infile, line);
if (isdigit(line[0])){
rpn_stack.push_back(atof(line.c_str()));
}
}
rpn_stack is a vector since I will not be using the built in stack libraries in C++.
The problem I am currently having is that the output is just outputting "12". Why is this?
Is there a way that I can traverse through the file character by character instead of reading as a line? Is it breaking because it finds a white space (would that be considered the EOF)?
EDIT:
The code has been rewritten to be as the following:
string line;
while (!infile.eof()){
getline(infile, line);
for (int i = 0; i < line.size(); i++){
if (isdigit(line[i])){
rpn_stack.push_back(atof(line.c_str()));
}
}
}
The output is 12 5 different times, which is obviously wrong. Not only are there 4 items in the txt document, but only one of them is a 12. Can someone give some insight?
This will read as many doubles from infile as possible (i.e. until the end of file or until it comes across a token that isn't a double), separated by whitespace.
for (double d; infile >> d;)
rpn_stack.push_back(d);
If you need parse line-by-line, as #ooga says you will need a two-stage reader that looks something like this:
for (std::string line; getline(infile, line);) {
std::istringstream stream{line};
for (double d; stream >> d;)
rpn_stack.push_back(d);
}
Bonus hint: don't use .eof()
I am currently working on a project that requires the assessment of mystery text files, and cross referencing them with signatures that are provided to me.
one issue I am facing is that we have gone over reading in files from a folder within the projects folder. (I'm using Visual Studios 2010)
I am provided with a simple 'data.txt' file, that contains an integer representing the number of file names of signatures; followed by that many signatures, then another integer representing the number of mystery texts; followed by that many mystery texts.
my question is, how does one read in a file, from a path given to them within another text document?
the 'data.txt' file is as follows:
13
signatures/agatha.christi.stats
signatures/alexandre.dumas.stats
signatures/brothers.grim.stats
signatures/charles.dickens.stats
signatures/douglas.adams.stats
signatures/emily.bronte.stats
signatures/fyodor.dostoevsky.stats
signatures/james.joyce.stats
signatures/jane.austen.stats
signatures/lewis.caroll.stats
signatures/mark.twain.stats
signatures/sir.arthur.conan.doyle.stats
signatures/william.shakespeare.stats
5
documents/mystery1.txt
documents/mystery2.txt
documents/mystery3.txt
documents/mystery4.txt
documents/mystery5.txt
one of the signature files is as follows(don't ask why my prof decided to use .stats, because I have no clue):
agatha christie
4.40212537354
0.103719383127
0.0534892315963
1
0.0836888743
1.90662947161
I cannot change the files, nor can I change the area in which they are saved.
I can easily read in the 'data.txt' file but cannot seem to find the signature files at all.
any help would be appreciated.
once I read in the signatures, I plan on saving them as structs in an array so I can reference them later in the project to compare them to the signatures of the mystery texts.
this program is using namespace std, if that matters to anyone...
Example as reading doubles.
File *file;
file = std::fopen(filename.c_str(), "r+b");
std::fread(&/*variableName*/, sizeof(double), 1, file);
Is this what you're looking for?
I assume your directory structure is as follows:
the_program
data.txt
signatures/...
documents/...
Then it should be straightforward to read the files:
std::ifstream in("data.txt");
std::vector<std::string> files;
int num_files;
in >> num_files;
for (unsigned i = 0; i < num_files; ++i) {
std::string file;
in >> file;
files.push_back(file);
}
// read mystery filenames
std::vector<std::string>::iterator it;
for (it = files.begin(); it != files.end(); ++it) {
std::ifstream sig(it->c_str());
// sig is your signature file. Read it here
}
#ptic12
your answer helped a lot, i managed to edit/manipulate it to get what i needed out of it.
i created a class for signatures to make it a bit more simple.
this code is written rather simply, and its longer than it needs to be, but it works, later in the project i plan on 'slimming down' a little bit
there are a few things missing from this of course, but it would be a long post if i included them
vector<Signature> MakeSignatures(string DataFile)
{
string SigFile="", MystFile="";
int NumSig=0, NumMystery=0;
ifstream infile(DataFile);// opens data.txt
infile >> NumSig;
vector<Signature> SigStorage;//creates a vector in which to store signature objects
for(int i=0; i <NumSig; i++)
{
infile >> SigFile;
Signature Sig(SigFile);
SigStorage.push_back(SigFile);
}
infile >> NumMystery;
for(int i=0; i < NumMystery; i++)
{
infile >> MystFile;
//not quite done here yet
//large part of project will be called here
}
return SigStorage;
}
and the constructor in the Signature class's .cpp
Signature::Signature(string SigFile)
{
ifstream in(SigFile);
while(!in.eof())
{
getline(in, AuthName); //gets author name
in >> AverageWord; //get next 5 floats
in >> TypeToken;
in >> HapaxLego;
in >> AverageNumber;
in >> SentenceCom;
}
}
hopefully this will help anyone who needs the same sort of help i did.
(didn't really help that the data.txt included a misspelled path, took a while to figure that one out)
I've been working on a class assignment for C++ and we're required to acquire input from a text file and assign those values to an array....one is a string, the second an int, and the third a double.
We've only been introduced to arrays and I don't know anything yet about pointers or linked lists, or any of the higher end stuff, so I feel like I'm somewhat limited in my options. I've worked all day trying to figure out a way to acquire input from the text file and assign it to the appropriate array. I've tried to use getline to read the input file and set a delimiter to separate each piece of data but I get an error when I try to use it more than once. From what I've read, this has to do with how I'm overloading the function but I'm at a loss at resolving it. Every explanation I've read about it goes beyond my current level of familiarity. Right now, I'm focused on this fragment of code:
for (int i = 0; i < EMP_NUM; i++) // Get input from text file for names.
getline(inFile, nameAr[i], '*');
for (int i = 0; i < EMP_NUM; i++) // Input for hours.
getline(inFile, hoursAr[i], '*');
for (int i=0; i < EMP_NUM; i++) // Input for hourly rate.
getline(inFile, hrateAr[i], '*');
I'm trying to use getline three times and write the data to three separate arrays, then make a series of calculations with them later and output them to another text file. The first instance of getline doesn't produce any compiler errors but the latter two do. I'm not quite sure of another solution to get the data into my arrays, so I'm at a loss. Any help would be great!
If I understand correctly you merely have three values in a file: a string, an int and a double. I assume they are delimited by whitespace.
If that is so then you don't need std::getline(). Rather, use the extraction operator:
std::ifstream file("input.txt");
std::string s;
if( ! (file >> s) ) { // a single word extracted from the file
// failure
}
int n;
// ...
1) Instead of three different iteration, use only one
2) Pass string object in getline instead of pointers
string buf;
for (int i = 0; i < EMP_NUM; i++) // Get input from text file for names.
{
getline(inFile, buf, '*');
nameAr[i] = buf;
getline(inFile, buf, '*'); //assuming delimiter is again *
hoursAr[i] = atoi(buf.c_str() ); //C way to doing it...however in c++ u have to use stringstreams....
getline(inFile, buf);
hrateAr[i] = atof(buf.c_str() );;
}
What do the compiler errors say? Are you sure that the error is caused by getline? Maybe it's not because the getline calls but because of multiple declarations of i.