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;
}
|