Getline help

I was wondering if there was a way to stop getline at 2 possible conditions.

I know that
getline(cin, num1, '/')
works but I want it to be able to stop at whitespace as well. Is that possible in C++?

It is of course possible, just not with std::getline.

Note that whitespace is the delimiter for regular operator>> on strings, so you could
1. getline on slashes and then re-parse on whitespace using >>

1
2
3
4
5
6
7
8
9
10
11
12
#include <sstream>
#include <iostream>

int main()
{
    for(std::string line; getline(std::cin, line, '/'); ) {
       std::istringstream ss(line);
       for(std::string tok; ss >> tok; ) {
            std::cout << "parser got: " << tok << '\n';
       }
    }
}

demo http://coliru.stacked-crooked.com/a/9af46c04229057c5


2: show off your C++ knowledge, redefine "whitespace" to include '/', and them use regular >>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <vector>
#include <locale>
#include <iostream>
struct slashspace : std::ctype<char> {
    static const mask* make_table()  {
        static std::vector<mask> v(classic_table(), classic_table() + table_size);
        v['/'] |=  space;  // comma will be classified as whitespace too
        return &v[0];
    }
    slashspace(std::size_t refs = 0) : ctype(make_table(), false, refs) {}
};

int main()
{
    std::cin.imbue(std::locale(std::cin.getloc(), new slashspace));
    for(std::string word; std::cin >> word; ) 
       std::cout << "parser got: " << word << '\n';
}

demo: http://coliru.stacked-crooked.com/a/0c41ae7cb4afb42d

3. read the whole thing into a string and parse it with any of the multitude of ways for parsing strings (regex, boost tokenizer, boost.split, spirit, whatever)

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

int main()
{
    std::string all{std::istreambuf_iterator<char>{std::cin}, {}};
    boost::char_separator<char> sep("/ \n\t");
    boost::tokenizer<boost::char_separator<char>> toks(all, sep);
    for(auto& s: toks) std::cout << "Parser got " << s << '\n';
}

demo http://coliru.stacked-crooked.com/a/a152dd858058699e

4. write a boring input loop that checks if the next char is whitespace or slash (perhaps encapsulated in operator>> of your token class)
Last edited on
1. create your own character set of delimiters and cast the delimiters to space
2. construct a std::locale object, x, with classic locale && your customized ctype (that includes the masked delimiters) inherited from std::ctype<char>
3. getline(file, line), then std::istringstream stream{line}
4. associate the locale x with stream using std::ios::imbue()
5. now you can std::copy the stream into any suitable standard library container using input iterators - since the delimiters have been cast into space they will
stop getline at 2 (or more) possible conditions

Below is some code from this forum, around last week of Oct, 2016 though I can't find the actual post:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <locale>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <fstream>
#include <sstream>
#include<string>
#include <vector>
#include<map>

using namespace std;
//From cppreference.com (mostly): Class ctype encapsulates character classification features. All stream input operations performed
//through std::basic_istream<charT> use the std::ctype<charT> of the locale imbued in the stream to identify whitespace characters
//for input tokenization. A locale, in turn, includes a ctype facet that classifies character types. Such a facet, incorporating
//further characters, could be as follows:
class my_ctype : public ctype<char>
{
    private:
        mask my_table[table_size];  //unspecified bitmask type;
    public:
        my_ctype(size_t refs = 0) : std::ctype<char>(&my_table[0], false, refs)
        {
            copy_n(classic_table(), table_size, my_table);
            my_table['-'] = (mask)space; //casts the delimiters to space;
            my_table['\''] = (mask)space;
            my_table['('] = (mask)space;
            my_table[')'] = (mask)space;
            my_table['!'] = (mask)space;
            my_table[','] = (mask)space;
            my_table['/'] = (mask)space;
            my_table['.'] = (mask)space;
            my_table['%'] = (mask)space;//sample array; can be expanded/modified depending on type of delimiters being handled;
        }
};

int main()
{
    fstream File;
    vector<string>v;
    File.open("F:\\test.txt");
    if(File.is_open())
    {
        while(!File.eof())
        {
            string line;
            getline(File, line);
            stringstream stream(line);
            locale x(locale::classic(), new my_ctype);
            //locale ctor using the classic() and my_ctype facet; locale destructor deletes the raw pointer;
            stream.imbue(x);//imbue sets the locale of the stream object;
            copy(istream_iterator<string>(stream),istream_iterator<string>(),back_inserter(v));
            //copies all elements in the range into the vector<string>;
            //derived, stringstream class, uses istream iterator;
            // std::ostream_iterator<std::string>(std::cout, "\n")//in case you want to print to screen;
        }
    }
    map<string, int> m;
    for(auto& itr: v)
    {   //creating the map with the vector elements;
        ++m[itr];
    }
    for(auto& itr: m)
    {
        cout<<itr.first<<" : "<<itr.second<<"\n";//printing the map;
    }
}
Last edited on
I think I should include all my code. I am trying to overload the input operator so that it takes in fractions and sends the components to class fraction. I feel like I am either making it overly complicated or am being over ambitious. The code works perfectly for only fractions but does not recognize inputs without a '/'.

fraction.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef FRAC_H
#define FRAC_H

using namespace std;

#include <iostream>

class fraction {
public:
	explicit fraction(int numerator = 0, int denomenator = 1);
	void setNumerator(int numerator);
	void setDenomenator(int denomenator);
	fraction operator+(const fraction& f) const;
	int getNumerator() const { return Numerator; };
	int getDenomenator() const { return Denomenator; };
	fraction operator-(const fraction& f) const;
private:
	int Numerator;
	int Denomenator;
};

#endif 


fraction.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>
#include "fraction.h"

using namespace std;

fraction::fraction(int numerator, int denomenator)
{
	numerator = 0;
	denomenator = 1;
}

void fraction::setNumerator(int numerator)
{
	Numerator = numerator;
}

void fraction::setDenomenator(int denomenator)
{
	Denomenator = denomenator;
}

fraction fraction::operator+(const fraction& f) const
{
	fraction sum;
	sum.Denomenator = Denomenator * f.Denomenator;
	sum.Numerator = ((Numerator * f.Denomenator) + (f.Numerator * Denomenator));
	return sum;
}

fraction fraction::operator-(const fraction& f) const
{
	fraction dif;
	dif.Denomenator = Denomenator * f.Denomenator;
	dif.Numerator = ((Numerator * f.Denomenator) - (f.Numerator * Denomenator));
	return dif;
}


client.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <string>
#include <sstream>
#include "fraction.h"

using namespace std;

istream& operator>>(istream &in, fraction &frac)
{
	string str_num;
	int num, denom;
	getline(in, str_num, '/');
	in >> denom;
	num = atoi(str_num.c_str());

	frac.setNumerator(num);
	frac.setDenomenator(denom);

	return in;
}

ostream& operator<<(ostream &out, const fraction &frac)
{
	return out << frac.getNumerator() << '/' << frac.getDenomenator();
}

int main()
{
	fraction F;
	fraction G;

	fraction J;
	fraction K;

	cout << "Enter 2 fractions: ";
	cin >> F >> G;

	cout << "Enter an integer and a fraction: ";
	cin >> J >> K;

	fraction H = F + G;
	fraction I = F - G;

	fraction L = J + K;
	fraction M = J - K;

	cout << "For 2 Fractions: " << endl;
	cout << "The sum is: " << H << endl;
	cout << "The difference is: " << I << endl;

	cout << "For 1 Integer and 1 Fraction: " << endl;
	cout << "The sum is: " << L << endl;
	cout << "The difference is: " << M << endl;
}
Nevermind, thank you for all your help. I figured it out. I was overthinking the process. I needed to just pass in a single integer into the class. The new code with reduction formula is:

fraction.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef FRAC_H
#define FRAC_H

using namespace std;

#include <iostream>

class fraction {
public:
	explicit fraction(int numerator = 0, int denomenator = 1);
	void setNumerator(int numerator);
	void setDenomenator(int denomenator);
	fraction operator+(const fraction& f) const;
	int getNumerator() const { return Numerator; };
	int getDenomenator() const { return Denomenator; };
	fraction operator-(const fraction& f) const;
private:
	int Numerator;
	int Denomenator;
};

#endif 


fraction.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>
#include "fraction.h"

using namespace std;

fraction::fraction(int numerator, int denomenator)
{
	setNumerator(numerator);
	setDenomenator(1);
}

void fraction::setNumerator(int numerator)
{
	Numerator = numerator;
}

void fraction::setDenomenator(int denomenator)
{
	Denomenator = denomenator;
}

fraction fraction::operator+(const fraction& f) const
{
	fraction sum;
	sum.Denomenator = Denomenator * f.Denomenator;
	sum.Numerator = ((Numerator * f.Denomenator) + (f.Numerator * Denomenator));
	for (int i = sum.Denomenator * sum.Numerator; i > 1; i--) {
		if ((sum.Denomenator % i == 0) && (sum.Numerator % i == 0)) {
			sum.Denomenator /= i;
			sum.Numerator /= i;
		}
	}
	return sum;
}

fraction fraction::operator-(const fraction& f) const
{
	fraction dif;
	dif.Denomenator = Denomenator * f.Denomenator;
	dif.Numerator = ((Numerator * f.Denomenator) - (f.Numerator * Denomenator));
	for (int i = dif.Denomenator * dif.Numerator; i > 1; i--) {
		if ((dif.Denomenator % i == 0) && (dif.Numerator % i == 0)) {
			dif.Denomenator /= i;
			dif.Numerator /= i;
		}
	}
	return dif;
}


client.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <string>
#include <sstream>
#include "fraction.h"

using namespace std;

istream& operator>>(istream &in, fraction &frac)
{
	string str_num;
	int num, denom;
	getline(in, str_num, '/');
	in >> denom;
	num = atoi(str_num.c_str());

	frac.setNumerator(num);
	frac.setDenomenator(denom);

	return in;
}

ostream& operator<<(ostream &out, const fraction &frac)
{
	return out << frac.getNumerator() << '/' << frac.getDenomenator();
}

int main()
{
	int j;

	fraction F;
	fraction G;

	fraction K;

	cout << "Enter 2 fractions: ";
	cin >> F >> G;

	cout << "Enter an integer and a fraction: ";
	cin >> j;
	cin >> K;

	fraction J{ j, 1 };

	fraction H = F + G;
	fraction I = F - G;

	fraction L = J + K;
	fraction M = J - K;

	cout << "For 2 Fractions: " << endl;
	cout << "The sum is: " << H << endl;
	cout << "The difference is: " << I << endl;

	cout << "For 1 Integer and 1 Fraction: " << endl;
	cout << "The sum is: " << L << endl;
	cout << "The difference is: " << M << endl;
}


output:

Enter 2 fractions: 1/2 1/4
Enter an integer and a fraction: 3 1/4
For 2 Fractions:
The sum is: 3/4
The difference is: 1/4
For 1 Integer and 1 Fraction:
The sum is: 13/4
The difference is: 11/4
Press any key to continue . . .
Last edited on
oh, that's what you're doing? There's no need to call atoi if you already have a stream at hand, let >> do its job:
1
2
3
4
5
6
7
8
9
10
11
12
istream& operator>>(istream &in, fraction &frac)
{
    char delim;
	int num, denom;
	in >> num >> delim >> denom;
	if(delim != '/') in.setstate(in.failbit);

	frac.setNumerator(num);
	frac.setDenomenator(denom);

	return in;
}

Topic archived. No new replies allowed.