mixed char and int stream from console

i started learning c++ along time ago. i got fairly comfortable with the language then stepped away from it for a long time. now im sorta going over the basics and really trying to understand what im learning all over again. well why doing this i came across something that i didnt notice the first time around dealing with strings and int and streams recieved from a console.

I was reviewing basic loops. and made a counter that counted down using an int variable. this worked well when i was entering numbers like 56 and 1024. however when i added a comma in the console input to the integer it broke it. its like a mixed number string with a single character thrown in.

so what i did was use a string as a variable then use stringstream to extract the integer from it. this didnt work either because again of the comma.

so my question is when dealing with numbers recieved from the console if the number has a comma in it is there a simple way to handle this or do i have to explicitly parse the string and remove it.
Last edited on
closed account (4Gb4jE8b)
if it was me (a beginner mind you) i would likely parse it explicitly as you said. But you have a good question, so i shall be following this thread.
closed account (3hM2Nwbp)
The way that I would approach this (allowing users to input commas) would be to read the input in as a string as parse it from there. After removing the commas, you could use the standard method atoi (http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/) to convert the string into an integer.
The STL I/O system has stuff in it to do that for you, but it is horribly broken and often more trouble than it is worth. Just use a simple functor class:

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 <algorithm>
#include <functional>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>

template <typename T>
struct with_commas_t
  {
  T* value;
  with_commas_t( T& value ): value( &value ) { }
  };

template <typename T>
inline
with_commas_t <T>
with_commas( T& value )
  {
  return with_commas_t <T> ( value );
  }

template <typename T>
std::istream& operator >> ( std::istream& ins, const with_commas_t <T> & wc )
  {
  std::string s;
  ins >> s; // see note 1
  // see note 2
  s.erase( std::remove( s.begin(), s.end(), ',' ), s.end() );
  std::istringstream ss( s );
  ss >> *(wc.value);
  if (ss.bad()) ins.setstate( ss.rdstate() );
  return ins;
  }

using namespace std;

int main()
  {
  double n;

  cout << "Enter a number with commas: " << flush;
  cin >> with_commas( n );
  cin.ignore( numeric_limits <streamsize> ::max(), '\n' );

  cout << "You entered " << fixed << n << endl;

  return 0;
  }

Note 1: Using the extraction operator on a string has the possibility of extracting an invalid number, such as "abc".

Note 2: Notice also how there is no check that you are actually extracting a valid numeric sequence. The user could enter ",,,123,,4,5" and have it parsed as 12345.

If you need something more exacting than that, you'll have to properly parse numbers yourself. Alas.

Hope this helps.
closed account (3hM2Nwbp)
If I may hijack this thread for a second, could you explain why how the STL's I/O is broken? I'm just curious. Thanks
Last edited on
Thanks everyone. It seems that some type of parsing is needed as i expected. Duoas thanks for the extended example. it is always appreciated.
I wanted to play with it some more. :-)

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include <cctype>
#include <ciso646>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>

//----------------------------------------------------------------------------
template <typename T>
struct with_commas_t
  {
  T* value;
  with_commas_t( T& value ): value( &value ) { }
  };

//----------------------------------------------------------------------------
template <typename T>
inline
with_commas_t <T>
with_commas( T& value )
  {
  return with_commas_t <T> ( value );
  }

//----------------------------------------------------------------------------
template <typename T>
std::istream& operator >> ( std::istream& ins, const with_commas_t <T> & wc )
  {
  std::string        s;           // our cache of input data
  std::ios::fmtflags flags = ins.flags();
  int                nth   = 3;   // number of digits between commas

  // Negative numbers
  switch (ins.peek())
    {
    case '-': s.push_back( '-' );
    case '+': ins.get();
    }

  // If basefield is unset, we must check to determine the base
  if (!(flags & std::ios::basefield))
    {
    if (ins.peek() == '0')
      {
      ins.get();
      if (std::toupper( ins.peek() ) == 'X')
        {
        ins.get();
        flags |= std::ios::hex;
        }
      else flags |= std::ios::oct;
      }
    else flags |= std::ios::dec;
    }

  // Hexadecimal sequences separate in groups of four: 0xDEAD,BEEF
  if (flags & std::ios::hex) nth = 4;

  // Now that we know our input base, we'll need a valid input lookup table
  std::string valid(
    ",0123456789ABCDEFabcdef",
    (flags & std::ios::hex) ? 23 :
    (flags & std::ios::oct) ?  9 : 11
    );

  // The number may be floating point. (We'll catch non-radix 10 stuff later)
  if (!std::numeric_limits <T> ::is_integer)
    valid.append( (flags & std::ios::hex) ? ".Pp+-" : ".Ee+-" );

  // Here's our scanner
  try {
    bool found_point = false;
    int n = -1;          // number of characters since last ','
    int c = ins.peek();  // character read

    // A number may not begin with a comma
    if (ins.peek() == ',') throw 1;

    // Continue until an invalid character is next
    // Make sure that commas only come every fourth (fifth for hex) character
    while (valid.find( ins.peek() ) != std::string::npos)
      {
      c = ins.get();
      switch (c)
        {
        case '.':           if (found_point) throw 1;
                            found_point = true;
                            s.push_back( c );
        case ',':           if ((n < 0) or (n == nth)) n = 0;
                            else throw 1;
                            break;
        case 'e': case 'E': if (!(flags & std::ios::hex))
        case 'p': case 'P':   n = -1;
        default:            s.push_back( c );
                            if (n >= 0) n += 1;
        }
      }

    // Make sure that we didn't end with an invalid comma
    if ((!found_point and (n >= 0) and (n != nth))
    or  ( found_point and (c == ',')))
      throw 1;

    // Now that we've got our validated string, without the commas, convert it
    std::istringstream ss( s );
    ss.flags( flags );
    ss >> *(wc.value);
    if (ss.bad()) throw 1;
    }
  catch (int) { ins.setstate( std::ios::failbit ); }
  return ins;
  }

//----------------------------------------------------------------------------
int main()
  {
  using namespace std;

  typedef double number;
//  typedef int    number;

  number n = 0;

  cout << "Today's number type is: "
       << ((numeric_limits <number> ::is_integer) ? "int" : "double")
       << ".\n";

  cout << "Enter a number with commas: " << flush;
  cin.unsetf( ios::basefield ); 
  cin >> with_commas( n );
  cin.ignore( numeric_limits <streamsize> ::max(), '\n' );

  if (!cin) cout << "Fooey! ";
  cout << "You entered " << fixed << n << endl;

  cout << "Press ENTER to quit.\n";
  cin.ignore( numeric_limits <streamsize> ::max(), '\n' );

  return 0;
  }

I put in code to parse hexadecimal and octal floating point values, but apparently the STL streams only take floats in decimal... alas.

In any case, the design of the STL is still in very large experiment. The way that things like internationalization are handled still needs work -- the way it is right now gives some structure and guideline, but making it actually work is up to the implementors, and in many cases just doesn't really work right the way it is currently designed. It needs to be repaired.
In any case, the design of the STL is still in very large experiment. The way that things like internationalization are handled still needs work -- the way it is right now gives some structure and guideline, but making it actually work is up to the implementors, and in many cases just doesn't really work right the way it is currently designed. It needs to be repaired.


Hi Duoas, to a certain extent the above is true. But all in all STL is a commendable improvement and it is now part of Standard C++. But what I hate is the Standard C++ pass standards too slowly.

In comparison, other competing languages like Java, Android SDK etc their pace is faster and implementations more robust. Very often developers don't like to spend too much time doing infrastructure plumbing coding, what we want is to focus more on our application business functionalities and delegate other features to SDK built-in classes, methods etc.

Sad to say, Standard C++ is a bit slow in this aspect.
I copypasted this code out of an old program I still have.
This code allows hex, octal, and decimals:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int main(){
	int i;
	{
		char s[100];
		fgets(s,100,stdin);
		char *t;
		int len=strlen(s);
		while(t=strchr(s,','))memcpy(t,t+1,len-(t+1-s)+1);
		i=strtol(s,NULL,0);
	}
	printf("%d",i);
}

Topic archived. No new replies allowed.