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);
}
}
}
Related
int n;
std::cin >> n;
std::string s = "";
std::getline(cin, s);
I noticed that if I use cin, my program would hang the next time I reach the line getline(cin, rangeInput).
Since getline() is using cin, is that why it is causing the program to hang if I have previously used cin? What should I do if I want to get a line after using cin?
You need to clear the input stream - try adding the following after your cin:
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
The accepted answer to this question gives a good explanation of why/when this is required.
std::cin leaves an extraneous \n in the input stream. When you use std::getline(), you are retrieving that \n.
Although #WilliamLannen's answer works if you really need std::cin, you are better off using this method:
int n;
std::string sn;
std::stringstream ssn;
std::getline(std::cin, sn);
ssn << sn;
ssn >> n;
References
http://www.daniweb.com/software-development/cpp/tutorials/71858
int n;
std::cin >> n;
std::cin.get() //<--- use cin.get() here ...
std::string s = "";
std::getline(cin, s);
int n;
std::cin >> n;
std::string s = "";
std::getline(cin, s);
I noticed that if I use cin, my program would hang the next time I reach the line getline(cin, rangeInput).
Since getline() is using cin, is that why it is causing the program to hang if I have previously used cin? What should I do if I want to get a line after using cin?
You need to clear the input stream - try adding the following after your cin:
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
The accepted answer to this question gives a good explanation of why/when this is required.
std::cin leaves an extraneous \n in the input stream. When you use std::getline(), you are retrieving that \n.
Although #WilliamLannen's answer works if you really need std::cin, you are better off using this method:
int n;
std::string sn;
std::stringstream ssn;
std::getline(std::cin, sn);
ssn << sn;
ssn >> n;
References
http://www.daniweb.com/software-development/cpp/tutorials/71858
int n;
std::cin >> n;
std::cin.get() //<--- use cin.get() here ...
std::string s = "";
std::getline(cin, s);
I need to read from file a series of information that is separated by commas
example
Orionis, 33000, 30000, 18, 5.9
Spica, 22000, 8300, 10.5, 5.1
i'm having a hard time figuring out the getline structure to make this work. The CS tutor, in the lab, says to use a getline for this but i can't seem to make it work (visual studio doesn't recognize getline in this function)
#include <iostream>
#include <fstream>
#include "star.h"
#include <string>
using namespace std;
char getChoice();
void processSelection(char choice);
void processA();
(skipping crap you don't need)
static char filePath[ENTRY_SZ];
void processA() {
ifstream openFile;
long temp, test;
double lum, mass, rad;
char name;
cout << "Please enter the full file path" << endl;
cin >> filePath;
openFile.open(filePath, ios::in);
if (openFile.good() != true) {
cout << "this file path was invalid";
}
while (openFile.good())
{
star *n = new star;
// getline(openFile, name, ',');
star(name);
getline(openFile, temp, ',');
n->setTemperature(temp);
getline(openFile, lum, ',');
n->setLuminosity(lum);
getline(openFile, mass, ',');
n->setMass(mass);
cin >> rad;
n->setRadius(rad);
}
}
From what i'm reading online (including older posts) and what my CS tutor says this should work so any help will be appreciated.
The suggestion to use std::getline() is likely implying that you'd first read a std::string and then deal with the content of this std::string, e.g., using std::istringstream.
I'd suggest not to use std::getline() and, of course, to also check inputs after they are read. To deal with the comma separator after non-std::string fields I'd use a custom manipulator:
std::istream& comma(std::istream& in) {
if ((in >> std::ws).peek() == ',') {
in.ignore();
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
This manipulator skips leading whitespace (using the manipulator std::ws) and then simply checks if the next character is a comma. If so, the comma is extracted, otherwise the stream is set into failure mode and further attempts to read will fail until the failure state is dealt with (e.g., by using in.clear() and probably getting rid of any offending characters).
With this manipulator it is easy to read the respective values. Note, that when switching from formatted to unformatted input it is likely necessary that leading whitespace (e.g., in this case line breaks) need to be ignored. Also, the code below first attempts to read the values and uses them only when this attempt was successful: input shall always be checked after a read attempt was made, not before!
// ...
long temp;
double lum, mass, rad;
std::string name;
while (std::getline(in >> std::ws, name, ',')
>> temp >> comma
>> lum >> comma
>> mass >> comma
>> rad) {
// use the thus read values
}
I'm doing an exercise for the college and I have to compare a string added including the header <string>, and a character.
I have a text file with a few lines of data from a census, like
Alabama AL 4849377 Alaska AK 736732 Arizona AZ 6731484
I want to read the state name of each line with a string variable, but the comparison is the only thing that I am asking for, because is where I have the error.
I have this fragment of code:
struct Census{
string name;
int population, code;
};
struct States{
Census state;
};
typedef States Vector[US_STATES];
void loadCensus(ifstream & census, Vector stats){
int i=0;
string readData;
string line;
while (getline(census, line)) {
stringstream linestream(line);
while (linestream >> readData) {
if (linestream >> stats[i].state.name >>
stats[i].state.code >>
stats[i].state.population)
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;
i++;
}
}
}
How I should convert readData to an integer to assign stats[i].state.population=readData?
I get an error in line 17 in the linestream >> readData.
You want to use the getline() function instead.
I think ita a member function of ifstream or either compare the not readData to a string ("\n") - double quotation. Or put the read data into a string and check if the sting contains a '\n'.
census >> readData will read the next word (any group of non-whitespace characters) from the input. In order to do this, it will discard all whitespace on its hunt for the next word. '\n' is whitespace, so you will never read it with the >> operator without playing games you probably don't want to play.
Instead of >>, use std::getline to read a line and then use a std::stringstream to break the line up into words.
std::string line;
while (std::getline(census, line)) {
std::stringgstream linestream(line);
while (linestream >> readData) {
statistics.state[i]=readData;
i++;
}
}
But...
I do not believe statistics.state[i]=readData; does quite what you want to do. You probably want something more like:
std::string line;
while (std::getline(census, line)) {
std::stringstream linestream(line);
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
i++;
}
In this state becomes an array or vector of objects that probably looks something like
struct statestats
{
std::string name;
std::string abbreviation;
int population;
};
Breaking it down line by line
std::stringstream linestream(line);
Makes a stringstream. A string stream is a stream like cin and cout or a fstream, but it contains a string. The main use is to buffer and build strings with the same syntax you would use on another stream. In this case we are use it to split up the line into words.
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Needs to be handled in a few parts in a few parts. Over all it is an abbreviation of
if (linestream >> statistics.state[i].name &&
linestream >> statistics.state[i].abbreviation &&
linestream >> statistics.state[i].population)
Each stage of which reads from the linestream into a variable.
Next, the >> operator returns the stream being read, and this is used two ways in the example. The first allows chaining. The output of one >> is used as the input of the next, so if you look at >> as you would a function (and it is a function. See Stream extraction and insertion for more) you can think about it looking something like this:
linestream.read(statistics.state[i].name).read(statistics.state[i].abbreviation).read(statistics.state[i].population)
The >> syntax just makes it easier.
The next advantage you get from returning the stream is the stream can be tested to see if the stream is still good. It has a boolean operator that will return true if the stream is in a good state and can be used.
if(linestream)
{
good
}
else
{
bad
}
will enter good if the stream is open, has not reached the end of the stream, and has had no troubles reading or writing data.
Going back to our example
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Will enter the body of the if statement if the stream successfully read all three values from the stream. Which is not what we want. Ooops. I've corrected the above code already.
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
will enter the body of the if if at least one value was not read for any reason and print out an error message. Normally when there is an error you will need to clear the error before continuing, but in this case we've use the whole stream and are about to discard it.
Assuming no error occurred all of the data from this line has been read and there is no need to
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;
It seems that it's not separating the word within the space.
Trying to separate the words in between, and stored it in first and second.
cin >> name; //input name
stringstream file (name);
getline(file,first, ' '); //seperate the name with the first name and last name using space
getline(file,second, ' ');
Replace
cin >> name;
with
getline(cin, name); //input name
cin >> reads only upto the first space. You would have realized this if you done a cout << name; to check what's getting read - this is the first step of debugging.
When you read the initial input with cin >> name; that only reads up to the first white space character.
You then try to break that into two pieces at white space, which it doesn't contain.
Easy way:
cin >> first >> second;
Alternatively, if you start with std::getline(cin, name); instead of cin >> name;, then the rest should work correctly.