I have an assignment to create a record management system for a class project. When adding records I would like to first read into a vector the contents of my record file currently then perform additions to the file finally outputting back to the record file. However, I'm having a hard time wrapping my mind around how to structure this. I am currently using a dynamic array to store the data but when I try to put it into the vector I it won't let me because it's a pointer. I feel like I'm approaching this entirely wrong and could use some assistance. Here is my input function:
void student::input(istream& inF, student* stud, vector<student>& vect, int size)
{
//local variables
string first, middle, last, addressNum, addressStreet,
phone, gender, email, emContactFirst, emContactLast;
int ID, age;
string ph, emPhone;
while (inF)
{
for (int index = 0; index < size; index++){
inF >> first >> last >> middle;
stud->setName(first, last, middle);
inF >> ID;
stud->setId(ID);
inF >> age;
stud->setAge(age);
inF >> phone;
stud->setPhone(phone);
inF >> addressNum >> addressStreet;
stud->setAddress(addressNum, addressStreet);
inF >> gender;
stud->setGender(gender);
inF >> email;
stud->setEmail(email);
inF >> emPhone;
stud->setEmPhone(emPhone);
inF >> emContactFirst >> emContactLast;
stud->setEmContact(emContactFirst, emContactLast);
inF >> stud->gpa >> stud->hobbies >> stud->major
>> stud->probation;
if (inF.eof())
break;
else
stud++;
vect.push_back(stud);
}
}
}
Problems I see:
You are using while (inF) to break the loop. See Why is iostream::eof inside a loop condition considered wrong?.
You are using one pointer, stud to read all the values and storing the same pointer multiple times in vect. First of all, the compiler should produce an error. You cannot add a pointer to a vector of objects.
It's not clear why the function needs stud as an argument. You can just as easily use a local object in the function. Like this:
for (int index = 0; index < size; index++){
student stud;
if ( !(inF >> first >> last >> middle) )
{
// Deal with error.
}
stud.setName(first, last, middle);
...
}
It's better to check whether the calls to inF >> ... assigned anything successfully and not assume that it succeeded. Instead of:
inF >> first >> last >> middle;
use
if ( !(inF >> first >> last >> middle) )
{
// Deal with error.
}
I suggest changing all such calls.
Related
I am trying to pull two columns of data from a CSV file and dump the rest.
The errors I am receiving are:
C2296: '>>': illegal, left operand has type 'std::basic_istream> &(__thiscall std::basic_istream>::* )(_Elem *,std::streamsize)'
C3867: 'std::basic_istream>::read': non-standard syntax; use '&' to create a pointer to member
The data is formatted as such:
1928,44.50%,.......
I want the 1928 assigned into data.year, and the 44.50% assigned into data.yield, but not including the percent sign.
bool ReadData(MyData &data)
{
//Variable for reading data into file stream
ifstream inFile;
string trash;
char junk;
cout << "\nReading file . . .";
//Open data file
inFile.open("Data.csv");
//Read the first 18 lines, and throw it away
for (int i = 0; i < 18; ++i)
{
getline(inFile, trash);
}
//Read the necessary data into the arrays
for (int i = 0; i < SIZE; ++i)
{
//===============================================================
//This line is throwing 2 errors
//Goal: read first column of a simple integer into data.year, discard the comma, then read the second column of a double into data.yield, discard the percentage sign. infile.ignore(); to clear cin stream, getline(inFile, trash) to discard remainder of the lines.
inFile.read >> data.year[i] >> junk >> data.yield[i] >> junk >> trash >> endl;
//===============================================================
inFile.ignore();
getline(inFile, trash);
}
//Return false if file could not be opened
if (!inFile)
{
cout << "\n\nTechnical error! The file could not be read.";
return false;
}
else
{
cout << "\n\nFile opened successfully!";
return true;
}
inFile.close();
}
struct MyData
{
int year[SIZE];
int yield[SIZE];
double minYield;
double maxYield;
double avgYield;
};
Where am I going wrong?
The very first problem is reading a file line by line constant number of times, however you never know the size of the file. So, you should add another check to your for loop. The second problem is that you say the yield is an int but it is a double in the file. The third problem is reading formatted data is not something like you did. The following piece of code can work for you, or you can play a bit with the code.
for (int i = 0; i < SIZE && std::getline(infile, line); ++i) {
std::stringstream linestream(line);
std::string year, yield;
getline(linestream,year,',');
getline(linestream,yield,',');
yield.erase(std::remove(yield.begin(), yield.end(), '%'), yield.end()); // remove %
myData.year[i] = std::stoi( year ); // string to int
myData.yield[i] = std::stod( year ); // string to double
}
PS: Don't forget to include sstream library.
inFile.read >> data.year[i] >> junk >> data.yield[i] >> junk >> trash >> endl;
inFile.read is a function and has no operator >>, that's why you get the error. See https://en.cppreference.com/w/cpp/io/basic_istream/read
I'd suggest you try a different approach: Read the entire line and use a explode function to retrieve the individual elements. For example Is there an equivalent in C++ of PHP's explode() function?
I am receiving user input via inputstream and am storing it in a array, the below code describes my scenario:
istringstream iss(line);
// The below code is incorrect
while (iss >> i)
{
image[j].r = i;
image[j].g = i;// this should be the next character
image[j].b = i;// this should be the character after the previous one
j++
}
Problem: I want to read in the characters 3 at a time and store them in my array structure. How can I achieve this?
Note: image is an array of a certain kind of structures
You should just read the values you want directly:
size_t j = 0;
istringstream iss(line);
while (iss >> image[j].r >> image[j].g >> image[j].b) {
j++;
}
You need to ensure that image is big enough to hold all the values read.
The probably more flexible solution would be to use a std::vector instead of a raw array:
istringstream iss(line);
std::vector<MyStruct> image;
MyStruct item;
while (iss >> item.r >> item.g >> item.b) {
image.push_back(item);
}
Alright, this is for a project that's supposed to read in some code from a file with information on a student. However, it doesn't seem to be reading in anything from the file. I'm working in Visual Studio, and when I run it and pause at the end, I look at the vector I've created and it says there are no students in it. Student is a class I've created.
ifstream input;
string student_file;
cin >> student_file;
input.open(student_file);
double id_number = 0;
while (input >> id_number)
{
string name = "";
string address = "";
string phone_number = "";
cin.sync();
getline(input, name);
cin.sync();
getline(input, address);
cin.sync();
getline(input, phone_number);
Student * s = new Student(id_number, name, address, phone_number);
students.push_back(s);
}
The student class should be set up properly with a constructor and everything, so I'm assuming it's this code that's giving me the trouble. Even some direction on whether the problem is with the loop or my use of getlines would be helpful. Thanks!
This line:
while (input >> id_number)
extracts the integer from the input stream into id_number. When the extraction is finished, the newline character is still left in the stream. std::getline() is programmed to terminate extraction upon the discovery of the newline character (among other specifications).
To circumvent this situation, you need to discard the newline by using the std::ws manipulator. std::ws discards only leading whitespace from the stream. Newlines are considered whitespace. It is also advised that you check that your input succeeded by encasing the extraction in an if statement:
if (std::getline(input >> std::ws, name) &&
std::getline(input >> std::ws, address) &&
std::getline(input >> std::ws, phone_number))
Notice that I've also removed the std::cin.sync() between calls. Syncing the input buffer is not necessary in this context as stream insertion is not being performed, thus changes to the external sequence don't need to be regarded. Besides, the idea of syncing the input buffer of std::cin is non-nonsensical as std::cin is not affected whatsoever by the actions being taken on input.
And lastly, your vector that holds the student pointers should instead hold the actual objects themselves. This avoids the need for using dynamic memory allocation. The type of the vector should therefore be std::vector<Student>. And this is how you would construct the element in the vector:
students.emplace_back(id_number, name, address, phone_number);
Here is the full code:
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::ifstream input;
std::string student_file;
std::cin >> student_file;
input.open(student_file);
double id_number = 0;
std::vector<Student> students;
while (input >> id_number)
{
string name;
string address;
string phone_number;
if (std::getline(input >> std::ws, name) &&
std::getline(input >> std::ws, address) &&
std::getline(input >> std::ws, phone_number))
{
students.emplace_back(id_number, name, address, phone_number);
}
}
}
I want to parse a file which describes a set of data line by line. Each datum consists of 3 or four parameters: int int float (optional) string.
I opened file as ifstream inFile and used it in a while loop
while (inFile) {
string line;
getline(inFile,line);
istringstream iss(line);
char strInput[256];
iss >> strInput;
int i = atoi(strInput);
iss >> strInput;
int j = atoi(strInput);
iss >> strInput;
float k = atoi(strInput);
iss >> strInput;
cout << i << j << k << strInput << endl;*/
}
The problem is that the last parameter is optional, so I'll probably run into errors when it is not present. How can i check in advance how many parameters are given for each datum?
Furthermore,
string line;
getline(inFile,line);
istringstream iss(line);
seems a bit reduldant, how could I simplyfiy it?
Use the idiomatic approach in this situation, and it becomes much simpler:
for (std::string line; getline(inFile, line); ) {
std::istringstream iss(line);
int i;
int j;
float k;
if (!(iss >> i >> j)) {
//Failed to extract the required elements
//This is an error
}
if (!(iss >> k)) {
//Failed to extract the optional element
//This is not an error -- you just don't have a third parameter
}
}
By the way, atoi has some highly undesired ambiguity unless 0 is not a possible value for the string you're parsing. Since atoi returns 0 when it errors, you cannot know if a return value of 0 is a successful parsing of a string with a value of 0, or if it's an error unless you do some rather laborious checking on the original string you had it parse.
Try to stick with streams, but in situations where you do need to fall back to atoi type functionality, go with the strtoX family of functions (strtoi, strtol, strtof, etc). Or, better yet, if you're using C++11, use the stoX family of functions.
You could use a string tokenizer How do I tokenize a string in C++?
In particular: https://stackoverflow.com/a/55680/2436175
Side note: you do not need to use atoi, you could simply do:
int i,j;
iss >> i >> j;
(but this wouldn't handle alone the problem of optional elements)
I am reading text from a text file, but I never reach eof, which results in an
endless loop.
Here's what I wrote
static ifstream inF;
inF.open(file,ifstream::in);
cin.rdbuf(inF.rdbuf());
while (inF.good() && !inF.eof())
{
addStudent(students);
}
if (inF.is_open())
{
inF.close();
inF.clear();
}
Every Iteration of the loop I call addStudents, which handles only one line. That works fine for me. Basically I rad lines in the form of D 98 76.5 66 45 (Possibly) 12000
here's the code:
static void addStudent(vector<Student*> students)
{
char institution;
unsigned short id;
double gAverage, pGrade, salary;
cin >> institution;
switch (institution)
{
case ALICE:
cin >> id >> gAverage >> salary;
students.push_back(new Student(institution,id,gAverage,salary));
return;
case BOB:
cin >> id >> gAverage >> salary;
students.push_back(new Student(institution,id,gAverage,salary));
return;
case COLIN:
cin >> id >> gAverage >> pGrade >> salary;
students.push_back(new CollegeStudent(institution,id,gAverage,pGrade,salary));
return;
case DANNY:
cin >> id >> gAverage >> pGrade >> salary;
students.push_back(new CollegeStudent(institution,id,gAverage,pGrade,salary));
return;
}
}
When I get to the end of the file the loop keeps running, and addStudents (which returns void) does nothing. Any Ideas why?
Thanks!
Your file stream may share it's stream buffer with cin, but it doesn't share it's flags. So when you read using cin's operator>>, and it the reaches end of the file, cin sets it's own eof flag, but it doesn't set the flag in your ifstream(how could it? it has no knowledge of it).
This is an awfully silly way to read a file, why are you doing it like that? Why don't you just pass an istream reference into your addStudent function and read from that?