How to derive ints from formatted string in C++? - c++

In a program, lets say we get a set of integers from the user in the following format:
std::cout << "Enter the new color value as: (red,green,blue)" << std::endl;
string input;
std::cin >> input;
What would then be the most well-practiced way to derive the ints from the string for operation?

A simple method is to overload the operator>> in your struct:
struct Pixel
{
int red;
int green;
int blue;
friend std::istream& operator>>(std::istream& input, Pixel& p);
};
std::istream& operator>>(std::istream& input, Pixel& p)
{
char c;
input >> c; // '('
input >> p.red;
input >> c; // ','
input >> p.green;
input >> c; // ','
input >> p.blue;
input >> c; // ')'
return input;
};
This allows you to do something like this:
Pixel p;
std::cout << "Enter the new color value as: (red,green,blue)" << std::endl;
cin >> p;
You may want to add checks to the input method for correct syntax.

From the question and comments, I'll assume the starting point is a std::string like:
std::string color { " ( 123, 1, 45 ) " };
The goal is to substract those numbers and convert them into integers. Let's first remove the white spaces:
color.erase(std::remove_if(color.begin(), color.end(), ::isspace), color.end());
We can now extract the numbers as strings:
std::regex reg("\\,");
std::vector<std::string> colors(
std::sregex_token_iterator(++color.begin(), --color.end(), reg, -1),
std::sregex_token_iterator()
);
Finally, convert them to integers:
std::vector<int> integers;
std::transform(colors.begin(), colors.end(), std::back_inserter(integers),
[](const std::string& str) { return std::stoi(str); });

Related

how can I input a literal string into istream? [duplicate]

How to achieve scanf("%d # %d",&a,&b);sort of effect with cin in C++ ?
You can skip the # by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}
There isn't a direct function inside the istream class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline() to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}
Here's another way. You can classify # as a whitespace character through the std::ctype<char> facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}
You can skip the #, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

C++ Specify input from istream and test it

I have a class basically representing a tuple (double x, doubly y) and I have overloaded the << operator so I can print the class. Now I want to do the same for >>, so that it only supports following formats:
x, (x) and (x,y).
I jave following code:
std::ostream & operator<< (std::ostream &output, tuple &c){
output << "(" << c.x << "," << c.y << ")" << endl;
return output;
}
std::istream & operator>> (std::istream & input, tuple &c){
// Check for following patterns: x, (x) or (x,y)
}
Can I loop through input and regex match? In that case how?
Also how could I test that it's actually working, something like this
std::cin >> "(10.2,5.5)"
or do I need to read from a file to test?
Edit:
The answer given did solve this problem, but I wanted to add a way to test it as it might be to use of someone other than me:
tuple x(6,2);
stringstream ss;
ss << x;
ASSERT_EQUALS(ss.str(), "(6,2)\n");
Regex would just be unnecessary for a simple input task such as this. Here is how I would do it, without any checking for valid input or not, just parsing:
std::istream & operator>> (std::istream & input, tuple &c){
// Check for following patterns: x, (x) or (x,y)
char firstCharacter;
input >> firstCharacter; // See what the first character is, since it varies
if (firstCharacter == '(')
{ // The simplest case, since the next few inputs are of the form n,n)
char delimiters;
input >> c.x >> delimiters >> c.y >> delimiters;
// N , N )
// You also here have to check whether the above inputs are valid,
// such as if the user enters a string instead of a number
// or if the delimeters are not commas or parentheses
}
else if (isdigit(firstCharacter) || firstCharacter == '-')
{ // For negative numbers
char delimiters;
input.unget(); // Put the number back in the stream and read a number
input >> c.x >> delimiters >> delimiters >> c.y >> delimiters;
// N , ( N )
// You also here have to check whether the above inputs are valid,
// such as if the user enters a string instead of a number
// or if the delimeters are not commas or parentheses
}
else
{
// Handle some sort of a parsing error where the first character
// is neither a parenthesis or a number
}
return input;
}
A little late to the party, but here is regex solution. While it is not pretty, it allows for negative numbers input as well as scientific notation. It also will tolerate spaces between numbers:
#include <iostream>
#include <regex>
#include <tuple>
std::istream &operator>> (std::istream &input, std::tuple<double, double> &t)
{
std::smatch m;
std::string s;
if (std::getline(input, s))
{
if (std::regex_match(s, m, std::regex(R"(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*)"))) //x
t = std::move(std::make_tuple(std::stod(m[1]), 0.0));
else if (std::regex_match(s, m, std::regex(R"(\s*\(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*\)\s*)"))) //(x)
t = std::move(std::make_tuple(std::stod(m[1]), 0.0));
else if (std::regex_match(s, m, std::regex(R"(\s*\(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*,\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*\)\s*)"))) //(x,y)
t = std::move(std::make_tuple(std::stod(m[1]), std::stod(m[2])));
}
return input;
}
int main()
{
std::tuple <double, double> t;
std::cout << "Enter data in format num, (num) or (num1,num2): ";
std::cin >> t;
std::cout << "Tuple 0: " << std::get<0>(t) << std::endl;
std::cout << "Tuple 1: " << std::get<1>(t) << std::endl;
return 0;
}

how to fill an array from a text file in c++

Hi I'm trying to fill in an array of a class object I created. Input is from a text file. The text file has strings and numbers. I got the first set of info in but the rest of the file won't read in, any thoughts would be appreciated!
class mess
{
private:
string name;
float age, weight, height;
public:
void setname(string a) {name=a;}
void setage(float b){age=b;}
//etc.
string getname(){return name;}
float getage(){return age;}
//etc.
}
ifstream input;
input.open("test.txt");
mess people[2]
string str;
float num;
for(inti=0; i<2; i++)
{
getline(input,str,'\n');
people[i].setname(str);
input >> num;
people[i].setage(num);
input >> num;
people[i].setweight(num);
input >> num;
people[i].setheight(num);
}
for(inti=0; i<2; i++)
{
cout << people[i].getname() << endl;
cout << people[i].getage() << endl;
cout << people[i].getweight() << endl;
cout << people[i].getheight() << endl;
}
test.txt
jack
17 150.3 5.10
Amy
18 110.4 5.11
Output:
Jack
17
150.3
5.10
(blank)
0
0
0
The problem here is that when you use the input operator >> it will leave the newline after the last number for the first record. This means that the next getline call will read that newline as an empty line, and then the numbers will fail to read.
There are a couple of ways to solve this. The first is to discard all text in the input until newline after reading the last number in the record. For this you can do e.g.
// All other input in loop
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Read about the ignore function.
Another way is to read the second line, complete, and put it into an std::istringstream and then read out the numbers from it:
// Reading the name...
std::string numbers;
std::getline(input, numbers);
std::istringstream istr(numbers);
istr >> num;
people[i].setage(num);
// ...
Also note that the third argument to std::getline already defaults to a newline, so if you're using it to read lines, then you don't need to provide it.
I suggest you overload operators << and >> in your class:
class mess
{
private:
string name;
float age, weight, height;
public:
void setname(string a) {name=a;}
void setage(float b){age=b;}
//etc.
string getname(){return name;}
float getage(){return age;}
//etc.
friend std::istream& operator>>(std::istream& inp, mess& m);
friend std::ostream& operator<<(std::ostream& out, const mess& m);
}
std::istream& operator>>(std::istream& inp, mess& m)
{
std::getline(inp, m.name);
inp >> m.age;
inp >> m.weight;
inp >> m.height;
return inp;
}
std::ostream& operator<<(std::ostream& out, const mess& m)
{
out << m.name << endl;
out << m.age << endl;
out << m.weight << endl;
out << m.height << endl;
return out;
}
This simplifies your input to:
std::vector<mess> people;
mess p;
while (input_file >> p)
{
people.push_back(p);
}
Your output looks like:
for (unsigned int i = 0; i < people.size(); ++i)
{
cout << people[i];
cout << "\n";
}
I would define the operator<< to write out an object of your class. Then defined the operator>> to read an object of your class. Then you can use std::istream_iterator to read the values directly into the container:
class M
{
private:
string name;
float age;
float weight;
float height;
public:
<STUFF>
friend std::ostream& operator<<(std::ostream& s, M const& data)
{
return s << data.age << " "
<< data.weight << " "
<< data.height << " "
// Put name on the edn because it may contain space.
// So we want to read it off using std::getline
<< data.name << "\n";
}
friend std::istream& operator>>(std::istream& s, M& data)
{
s >> data.age >> data.wight >> data.height;
std::getline(s, data.name);
// Strip leading space (see output operator)
data.name = data.name.substring(1);
return s;
}
};
Then easy to use in most containers.
int main()
{
std::ifstream f("data");
// OK.
// A vector is not actually an array.
// But you can use the same principle with a manual loop.
// as std::istream_iterator is just a normal iterator.
std::vector<M> data(std::istream_iterator<M>(f), std::istream_iterator<M>());
}

Skipping expected characters like scanf() with cin

How to achieve scanf("%d # %d",&a,&b);sort of effect with cin in C++ ?
You can skip the # by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}
There isn't a direct function inside the istream class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline() to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}
Here's another way. You can classify # as a whitespace character through the std::ctype<char> facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}
You can skip the #, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

C++ getline() delimitter

Hey I am trying to read in the following lines using a getline
(15,0,1,#)
(2,11,2,.)
(3,20,0,S)
I want to be able to just extract the integers as ints and the characters as char, but I have no idea how to only extract those.
It seems you could read off the separators, i.e., '(', ')', and ',' and then just use the formatted input. Using a simple template for a manipulator should do the trick nicely:
#include <iostream>
#include <sstream>
template <char C>
std::istream& read_char(std::istream& in)
{
if ((in >> std::ws).peek() == C) {
in.ignore();
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
auto const open_paren = &read_char<'('>;
auto const close_paren = &read_char<')'>;
auto const comma = &read_char<','>;
int main()
{
int x, y, z;
char c;
std::istringstream in("(1, 2, 3, x)\n(4, 5, 6, .)");
if (in >> open_paren >> x
>> comma >> y
>> comma >> z
>> comma >> c
>> close_paren) {
std::cout << "x=" << x << " y=" << y << " z=" << z << " c=" << c << '\n';
}
}
Compare the value you get from getline()'s hexadecimal value, and run some if statements to compare to ASCII. That will tell you if you grabbed a number, letter, or symbol.