Let's say I want the user to input an integer but he enters a double or a character value, how do I check that the the user has input the correct type.
string line;
getline(cin, line);
// create stringstream object called lineStream for parsing:
stringstream lineStream(line);
string rname;
double res;
int node1,node2;
lineStream >> rname >> res >> node1 >> node2;
How do I check for valid input type?
You check the stream is OK:
if (lineStream >> rname >> res >> node1 >> node2)
{
// all reads worked.
}
You may want to check for garbage on the end.
if (lineStream >> rname >> res >> node1 >> node2)
{
char x;
if (lineStream >> x)
{
// If you can read one more character there is junk on the end.
// This is probably an error. So in this situation you
// need to do somethings to correct for this.
exit(1);
}
// all reads worked.
// AND there is no junk on the end of the line.
}
Comment expanded.
From the comments below:
if i input an integer for rname, it still works. for exmaple:
string line; getline(cin, line);
stringstream lineStream(line); // created stringstream object called lineStream for parsing
string rname;
if (lineStream >> rname) { cout << "works"; }
Lets assume there is some properties about rname that allow us to distinguish it from a number. For example: it must be a name. i.e. it must only contain alpha characters.
struct Name
{
std::string value;
friend std::istream& operator>>(std::istream& s, Name& data)
{
// Read a word
s >> data.value;
// Check to make sure that value is only alpha()
if (find_if(data.value.begin(), data.value.end(), [](char c){return !isalpha(c);}) != str.end())
{
// failure is set in the stream.
s.setstate(std::ios::failbit);
}
// return the stream
return s;
}
};
Now you can read a name.
Name rname;
if (lineStream >> rname) { cout << "works"; }
This will fail if you enter an integer for rname.
Stretch Answer
If you have multiple lines of the same information you want to read. Then it is worth wrapping it in a class and defining a input stream operator.
strcut Node
{
Name rname;
double res;
int node1;
int node2;
friend std::istream& operator>>(std::istream& s, Node& data)
{
std::string line;
std::getline(s, line);
std::stringstream linestream(line);
lineStream >> data.rname >> data.res >> data.node1 >> data.node2;
if(!linestream)
{
// Read failed.
s.setstate(std::ios::failbit);
}
return s;
}
};
Now it becomes easy to read the lines in a loop:
Node n;
while(std::cin >> n)
{
// We read another node successfully
}
Since string 123 will also be considered as string, and not an integer, so the better way is to iterate the string to end, until we find any non-digit character. here's how you may do it:
bool is_number(const std::string& s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
Read node1 and node2 into strings first, then validate with a regular expression.
#include <regex>
...
string node1_s, node2_s;
linestream >> rname >> node1_s >> node2_s
if (regex_match(node1_s, regex("[+-]?[0-9]+") {
/* convert node1_s to int */
} else {
/* node1_s not integer */
}
/* Do same for node2_s */
Related
I need to read a .csv file, put the information into a struct, then insert the struct into a binary file. Each column means:
(int)Year; (int)Rank; (char*)University's Name; (float)Score; (char*)City; (char*)Country
My .csv file looks like:
2018,1,Harvard University,97.7,Cambridge,United States
2018,2,University of Cambridge,94.6,Cambridge,United Kingdom
2018,3,University of Oxford,94.6,Oxford,United Kingdom
2018,4,Massachusetts Institute of Technology (MIT),92.5,Cambridge,United States
2018,5,Johns Hopkins University,92.1,Baltimore,United States
As you can see, it contains the five best universities in the world.
The issue is my code can read neither the integers (perhaps due to commas in the file) nor the char[30].
struct Data
{
int year;
int rank;
char name[30];
float score;
char city[30];
char country[30];
};
`Data *universitie = new Data [5];
ifstream read ( "myFile.csv" );
int i = 0;
while ( !read.eof() ) {
read >> universitie[i].year;
read >> universitie[i].rank;
read.getline (universitie[i].name, 30, ','); //here a segmentation fault happened
read >> universitie[i].score;
read.getline (universitie[i].city, 30, ','); //here a segmentation fault happened
read.getline (universitie[i].country, 30, '\n'); //here a segmentation fault happened
i++;
}
read.close ();
I'm actually using char[30] for name, city and country instead of string because then I'll write this struct array into a binary file.
How can I properly read the integers until the comma?
How to read a character array from a file using getline() with delimeters like ,?
This is for my Computer Science course's task.
The usual technique with CSV files is to model a record with a class, then overload operator>> to input a record:
struct Record
{
int year;
int rank;
std::string name;
double score;
std::string city;
std::string country;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
char comma;
input >> r.year;
input >> comma;
input >> r.rank;
input >> comma;
std::getline(input, r.name, ',');
input >> r.score;
std::getline(input, r.city, ',');
std::getline(input, r.country, '\n');
return input;
}
A use case for the above could look like:
std::vector<Record> database;
ifstream data_file ( "myFile.csv" );
Record r;
while (data_file >> r)
{
database.push_back(r);
}
Note: I have changed your char[] to std::string for easier handling. Also, I changed the while loop condition.
//Open the file
Data *universitie = new Data [5];
int i = 0;
std::string line;
while(std::getline(file, line))
{
std::stringstream ss;
ss << line;
ss >> universitie[i].year;
ss.ignore();
ss >> universitie[i].rank;
ss.ignore();
ss >> universitie[i].name;
ss.ignore();
ss >> universitie[i].score;
ss.ignore();
ss >> universitie[i].city;
ss.ignore();
ss >> universitie[i].country;
i++;
}
This is a solution with std::stringstream. The ignore() function is used to skip the ',' between the entries.
Also in my opinion is better to use the C++ std::string class instead of char[30]. If at some point you need c string then you can use the c_str() function.
Want to read string input (from array) and save it into different variables. My code:
const string myArray[]{"4", "1 - 5", "Text_1", "Text_2", "Text_3"};
//method, which saves 1st item from myArray into line1, 2nd into line2 and the rest into line3
void Parts::saveParts(const string *myArray, int rows) {
// rows == length of myArray
istringstream iss(*myArray);
int id;
char del;
if (iss >> id >> del) {
line = id;
string rawLine2;
if (iss >> rawLine2 >> del) {
this->setLine(rawLine2);
string text;
textItems[numberOfRows-2];
if (iss >> text) {
// how can I read the rest of myArray? What should I set as 'del'?
// 'Text_1' should go into 'textItems[0]' and so on
}
}
}
else {
// here I want to throw an exception, if reading (iss >> id >> del) goes wrong (in case of id is not an integer
}
}
If the code structure itself is correct, can you direct me to completing signed if statement?
I'm currently trying to load a text file into struct data members. Each number is separated by a comma.
#include<string>
#include<sstream>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
};
int main(){
string fileName;
string line;
pass Pass;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName);
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
}
Here is an example of the text file.
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
3.425838997,1.603712153,0
3.502553324,0.998192867,0
3.917348666,1.49223429,0
4.391605986,0.831661367,0
4.947059678,0.8557003,0
5.429305232,2.42029408,0
The data in the text file follows this format:
arrivalTime,serviceTime,classType
As you can see i have split the line up and stored it in "line" using the comma delimiter, but i am unsure how to load each number into the struct in the while loop.
Any help would be appreciated.
Define an istream operator >> for your struct. Something like
struct pass {
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
friend std::istream & operator >>(std::istream & in, pass & p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
Then, simply
pass Pass;
fin >> Pass;
while (getline(fin, line))
{
sscanf(line.c_str(), "%lf,%lf,%d", &arrivalTime, &serviceTime, &classType);
}
This loop is wrong:
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
You are reading everything from the stream up to the next ',' character, then trying to read more from the stream.
Given the input file:
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
Your program reads "0.951412936" into line (and discards the ',') then tries to read the next input into Pass[0].arrivalTime but the next input is 2.131445423, which was meant to be the serviceTime (which you already read into line).
As Shreevardhan suggests you can define an operator for reading your struct from a stream. I would make it more reliable like so:
struct ExpectedChar { char expected; };
// read a character from a stream and check it has the expected value
std::istream& operator>>(std::istream& in, const ExpectedChar& e)
{
char c;
if (in >> c)
if (c != e.expected) // failed to read expected character
in.setstate(std::ios::failbit);
return in;
}
// read a struct pass from a stream
std::istream& operator>>(std::istream& in, pass& p)
{
ExpectedChar comma{ ',' };
in >> p.arrivalTime >> comma >> p.serviceTime >> comma >> p.classType;
return in;
}
This will stop reading if the input file does not meet the expected format. Now you can do:
while (fin >> Pass)
{
// do something with each pass
}
if (!fin.eof()) // stopped reading before end-of-file
throw std::runtime_error("Invalid data in input file");
This will keep reading a pass from the file until reading fails, either because it reached the end of the file, or because there was some bad data in the file. If there is bad data it throws an exception.
#include<string>
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime;
double serviceTime;
int classType;
friend std::istream & operator >>(std::istream &in, pass &p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
int main(){
string fileName;
string line;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName.c_str(), ifstream::in);
vector<pass> passes;
pass Pass;
while (fin>>Pass)
passes.push_back(Pass);
for(vector<pass>::const_iterator iter = passes.begin();
iter != passes.end();
++iter)
std::cout<<iter->arrivalTime<<" "<<iter->serviceTime<<" "
<<iter->classType<<std::endl;
}
Here is hint;
string line;
string temp;
string::size_type sz;
while (getline(cin, line))
{
istringstream ss( line );
getline( ss, temp, ',' );
double arrivalTime = stod(temp, &sz);
getline( ss, temp, ',' );
double serviceTime = stod(temp, &sz);
getline( ss, temp, ',' );
double classType = stod(temp, &sz);
cout << arrivalTime << ' '
<< serviceTime << ' '
<< classType << endl;
}
I am reading a text file in c++, this is example of some lines in it:
remove 1 2 cost 13.4
How could I disregard all things except two integers after remove, "1" and "2" and put them in two integer variable?
my incomplete code:
ifstream file("input.txt");
string line;
int a, b;
if(file.is_open())
{
while (!file.eof())
{
getline (file, line);
istringstream iss(line);
if (line.find("remove") != string::npos)
{
iss >> a >> b; // this obviously does not work, not sure how to
// write the code here
}
}
}
Here are a few options:
Use the stringstream created for the line to find the remove token and parse the next two integers. In other words, replace this:
if (line.find("remove") != string::npos)
{
iss >> a >> b; // this obviously does not work, not sure how to
// write the code here
}
with this:
string token;
iss >> token;
if (token == "remove")
{
iss >> a >> b;
}
Create a stringstream for the rest of the line (6 is the length of the "remove" token).
string::size_type pos = line.find("remove");
if (pos != string::npos)
{
istringstream iss(line.substr(pos + 6));
iss >> a >> b;
}
Call the seekg method on the line stringstream to set the input position indicator of the stream after the "remove" token.
string::size_type pos = line.find("remove");
if (pos != string::npos)
{
iss.seekg(pos + 6);
iss >> a >> b;
}
I have a file:
name1 8
name2 27
name3 6
and I'm parsing it into vector. This is my code:
int i=0;
vector<Student> stud;
string line;
ifstream myfile1(myfile);
if (!myfile1.is_open()) {return false;}
else {
while( getline(myfile1, line) ) {
istringstream iss(line);
stud.push_back(Student());
iss >> stud[i].Name >> stud[i].Grade1;
i++;
}
myfile1.close();
}
I need to check if the stud[i].Grade1 is int. If it isn't it return false.
File can contain:
name1 haha
name2 27
name3 6
How can I do it?
EDIT:
I have tried another way (without getline) and it seems to work. I don't understand why :/
int i=0;
vector<Student> stud;
ifstream myfile1(myfile);
if (!myfile1.is_open()) {return false;}
else {
stud.push_back(Student());
while( myfile1 >> stud[i].Name ) {
if(!(myfile1 >> stud[i].Points1)) return false;
i++;
stud.push_back(Student());
}
myfile1.close();
}
If type of Grade1 in numerical such as int, Use std::istringstream::fail() :
// ...
while( getline(myfile1, line) ) {
istringstream iss(line);
stud.push_back(Student());
iss >> stud[i].Name;
iss >> stud[i].Grade1;
if (iss.fail())
return false;
i++;
}
myfile1.close();
}
// ...
It could look like this:
std::vector<Student> students;
std::ifstream myfile1(myfile);
if (!myfile1.is_open())
return false;
std::string line;
while (std::getline(myfile1, line))
{
// skip empty lines:
if (line.empty()) continue;
Student s;
std::istringstream iss(line);
if (!(iss >> s.Name))
return false;
if (!(iss >> s.Grade1))
return false;
students.push_back(s);
}
just note that iss >> s.Grade1 will succeed not only for decimal, but also for octal and hexadecimal numbers too. To make sure that only decimal value will be read, you could read it into the temporary std::string object and validate it before you use it to retrieve the number. Have a look at How to determine if a string is a number with C++?