With scanf there's, usually, a direct way to take formatted input:
1) line with a real number higher than 0, and less than 1. Ending on 'x', e.g: 0.32432523x
scanf("0.%[0-9]x", &number);
2) line represents an addition in the format :30+28=fifty-eight
scanf(":%d+%d=%99s", &number1, &number2, &total);
What is the cin solution, using only the standard library?
You can make a simple class to verify input.
struct confirm_input {
char const *str;
confirm_input( char const *in ) : str( in ) {}
friend std::istream &operator >>
( std::istream &s, confirm_input const &o ) {
for ( char const *p = o.str; * p; ++ p ) {
if ( std::isspace( * p ) ) {
std::istream::sentry k( s ); // discard whitespace
} else if ( (c = s.get() ) != * p ) {
s.setstate( std::ios::failbit ); // stop extracting
}
}
return s;
}
};
usage:
std::cin >> x >> confirm_input( " = " ) >> y;
Use the >> operator to read from cin.
int number1, number2;
std::string text;
char plus, equals;
std::cin >> number1 >> plus >> number2 >> equals >> text;
if (!std::cin.fail() && plus == '+' && equals == '=' && !text.empty())
std::cout << "matched";
It's not as good as scanf because you'd have to verify any literals that were in the scanf string yourself. Doing it with streams will almost certainly be a lot more lines of code than scanf.
I would use scanf.
Related
bool isDigital(char c) { return ('0' <= c && c <= '9'); }
void DigitalToken( char digitToken[50], char ch ) {
digitToken[0] = ch;
char input = '\0';
cin >> input;
int i = 0;
while ( ( input != ' ' ) && ( input != '\t' ) && ( input != '\n' ) ) { // got a infinite loop
i++;
digitToken[i] = input;
cin >> input;
} // while ( input != ' ' && input != '\t' && input != '\n' )
} // DigitalToken()
int main() {
char ch = '\0';
while ( cin >> ch ) {
if ( isDigital(ch) ) {
char* digitToken = new char[50]();
DigitalToken(digitToken, ch);
cout << digitToken;
delete[] digitToken;
} // else if
} // while
} // main()
I don't understand why I got a infinite loop in DitgitalToken function.
When I input 123, it should be output 123.
I watched it for a long time, but I still don’t know why and how to fix it.
Instead of cin >> input; use input = cin.get();.
You have to be careful while using cin with characters or strings. It treats spaces, tabs, newlines as end of the input and hence do not treat them as input themselves.
Your program blocks in the while loop on cin >> input; after it read "123". As #Arty suggested use cin.get() instead as whitespace is stripped by default. You can also use cin >> noskipws; prior to executing cin >> input;. See skipws.
I have a text file that contains over 5000 lines with data (lottery draw results for Lotto). Each line has the form: number. day.month.year number1,number2,number3,number4,number5,number6
Five sample lines:
27.01.1957 8,12,31,39,43,45
03.02.1957 5,10,11,22,25,27
10.02.1957 18,19,20,26,45,49
17.02.1957 2,11,14,37,40,45
24.02.1957 8,10,15,35,39,49
I have also:
struct Lotto
{
short number_drawing;
char day;
char month;
short year;
char tab[6];
};
I have to write data from this text file into a binary file as struct Lotto.
I have already run out of ideas.
I have beeng trying since few days but my program still doesn't work properly :(
I try to load although one line :)
int main()
{
ifstream text("lotto.txt", ios::in);
ofstream bin("lottoBin.txt", ios::binary | ios::out);
Lotto zm;
short number_drawing;
char day;
char month;
short year;
char tab[6];
char ch;
int records = 0;
while (!text.eof())
{
text >> zm.number_drawing >> ch >> zm.day >> ch >> zm.month >>
ch >> zm.year >> zm.tab[0] >> ch >> zm.tab[1] >> ch >> zm.tab[2] >>
ch >> zm.tab[3] >> ch >> zm.tab[4] >> ch >> zm.tab[5];
records++;
}
cout << "All records: " << records << endl;
Here are some observations that might help you:
You will not be able to directly read a number into a char. Use an intermediate integer.
Define a function to read a record: bool read( std::istream&, Lotto& )
Your while should call the above function: while ( read( is, lotto ) )
A starting point:
bool read( std::istream& is, Lotto& lotto )
{
short t;
char c;
// read data
//
is >> t;
lotto.number_drawing = t;
is >> c;
if ( c != '.' )
return false;
//
is >> t;
lotto.day = char( t );
is >> c;
if ( c != '.' )
return false;
// read rest of fields...
// end of line
while ( is.get( c ) && isspace( c ) && c != '\n' )
;
if ( ! is.eof() && c != '\n' )
return false;
// check data
if ( lotto.month > 12 )
return false;
// check rest of data...
return is.good();
}
int main()
{
ifstream is( "yourfile.txt" );
if ( ! is )
return -1;
Lotto lotto;
while ( read( is, lotto ) )
{
// ...
}
if ( !is.eof() )
return -1;
return 0;
}
I am trying to read complex numbers of the form x + y*i from a file which looks like this:
2 + 3i
4 + 5i
If I implement it like this it only works for the first line and I would like to be able to read again in the same manner the second line. Any ideas?
istream& operator>>(istream& in, Complex& c) {
in >> c.r >> c.i;
return in;
}
EDIT: Do not throw exceptions directly, as this is not the usual way of doing thing with iostreams.
EDIT: Process the sign character separately so that spaces in the input are allowed.
A quick and dirty solution:
istream& operator>>(istream& in, Complex& c)
{
char sign;
char suffix;
in >> c.r >> sign >> c.i >> suffix;
if ((sign != '+' && sign != '-') || suffix != 'i') {
in.setstate(ios::failbit);
}
if (sign == '-') {
c.i = -c.i;
}
return in;
}
You need to make sure that you read both the "+" and the "i" when processing the input.
The following implementation works:
struct complex
{
double r;
double i;
};
std::istream& operator>>(std::istream& in, complex& c)
{
char plus, i;
in >> c.r >> plus >> c.i >> i;
return in;
}
std::string initdata = "2 + 3i\n4 + 5i\n";
int main()
{
std::stringstream ss(initdata);
std::vector<complex> values;
std::istream_iterator<complex> begin(ss), end;
std::copy(begin, end, std::back_inserter<std::vector<complex>>(values));
}
I would do it like this :
#include <iostream>
struct complex
{
int r, i;
};
int main ()
{
complex co;
char c;
while ((std::cin >> co.r >> c /* '+' */ >> co.i >> c /* 'i' */))
{
std::cout << co.r << ' ' << co.i << "i\n";
}
}
This should work for you:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
std::istringstream s(
"2 + 3i\n"
"4 + 5i\n"
);
char sgn;
double r;
double i;
while(s >> r >> sgn >> i) {
if(s.get() != 'i') {
std::cerr << "Missing i\n";
return -1;
}
std::cout << r << sgn << i << "i\n";
}
return 0;
}
Note: The space before the imaginary part and the trailing i are breaking the input.
I need to print some data from stream - istringstream ( in main () ).
example:
void Add ( istream & is )
{
string name;
string surname;
int data;
while ( //something )
{
// Here I need parse stream
cout << name;
cout << surname;
cout << data;
cout << endl;
}
}
int main ( void )
{
is . clear ();
is . str ( "John;Malkovich,10\nAnastacia;Volivach,30\nJohn;Brown,60\nJames;Bond,30\n" );
a . Add ( is );
return 0;
}
How to do parsing this line
is.str ("John;Malkovich,10\nAnastacia;Volivach,30\nJohn;Brown,60\nJames;Bond,30\n");"
to name;surname,data?
This is somewhat fragile, but if you know your format is exactly what you posted, there's nothing wrong with it:
while(getline(is, name, ';') && getline(is, surname, ',') && is >> data)
{
is.ignore(); // ignore the new line
/* ... */
}
If you know the delimiters will always be ; and ,, it should be fairly easy:
string record;
getline(is, record); // read one line from is
// find ; for first name
size_t semi = record.find(';');
if (semi == string::npos) {
// not found - handle error somehow
}
name = record.substr(0, semi);
// find , for last name
size_t comma = record.find(',', semi);
if (comma == string::npos) {
// not found - handle error somehow
}
surname = record.substr(semi + 1, comma - (semi + 1));
// convert number to int
istringstream convertor(record.substr(comma + 1));
convertor >> data;
Given data format as "int,int,...,int,string,int", is it possible to use stringstream (only) to properly decode the fields?
[Code]
int main(int c, char** v)
{
std::string line = "0,1,2,3,4,5,CT_O,6";
char delimiter[7];
int id, ag, lid, cid, fid, did, j = -12345;
char dcontact[4]; // <- The size of <string-field> is known and fixed
std::stringstream ssline(line);
ssline >> id >> delimiter[0]
>> ag >> delimiter[1]
>> lid >> delimiter[2]
>> cid >> delimiter[3]
>> fid >> delimiter[4]
>> did >> delimiter[5] // <- should I do something here?
>> dcontact >> delimiter[6]
>> j;
std::cout << id << ":" << ag << ":" << lid << ":" << cid << ":" << fid << ":" << did << ":";
std::cout << dcontact << "\n";
}
[Output] 0:1:2:3:4:5:CT_6,0:-45689, the bolded part shows the stringstream failed to read 4 char only to dcontact. dcontact actually hold more than 4 chars, leaving j with garbage data.
Yes, there is no specific overload of operator >> (istream&, char[N]) for N and there is for char* so it sees that as the best match. The overload for char* reads to the next whitespace character so it doesn't stop at the comma.
You could wrap your dcontact in a struct and have a specific overload to read into your struct. Else you could use read, albeit it breaks your lovely chain of >> operators.
ssline.read( dcontact, 4 );
will work at that point.
To read up to a delimiter, incidentally, you can use getline. (get will also work but getline free-function writing to a std::string will mean you don't have to guess the length).
(Note that other people have specified to use get rather than read, but this will fail in your case as you do not have an extra byte at the end of your dcontact array for a null terminator. IF you want dcontact to be null-terminated then make it 5 characters and use 'get` and the null will be appended for you).
Slightly more robust (handles the ',' delimiter correctly):
template <char D>
std::istream& delim(std::istream& in)
{
char c;
if (in >> c && c != D) in.setstate(std::ios_base::failbit);
return in;
}
int main()
{
std::string line = "0,1,2,3,4,5,CT_O,6";
int id, ag, lid, cid, fid, did, j = -12345;
char dcontact[5]; // <- The size of <string-field> is known and fixed
std::stringstream ssline(line);
(ssline >> id >> delim<','>
>> ag >> delim<','>
>> lid >> delim<','>
>> cid >> delim<','>
>> fid >> delim<','>
>> did >> delim<','> >> std::ws
).get(dcontact, 5, ',') >> delim<','>
>> j;
std::cout << id << ":" << ag << ":" << lid << ":"
<< cid << ":" << fid << ":" << did << ":";
<< dcontact << "\n";
}
The problem is that the >> operator for a string
(std::string or a C style string) actually implements the
semantics for a word, with a particular definition of word. The
decision is arbitrary (I would have made it a line), but since
a string can represent many different things, they had to choose
something.
The solution, in general, is not to use >> on a string, ever.
Define the class you want (here, probably something like
Symbol), and define an operator >> for it which respects its
semantics. You're code will be a lot clearer for it, and you
can add various invarant controls as appropriate. If you know
that the field is always exactly four characters, you can do
something simple like:
class DContactSymbol
{
char myName[ 4 ];
public:
// ...
friend std::istream&
operator>>( std::istream& source, DContactSymbol& dest );
// ...
};
std::istream&
operator>>( std::istream& source, DContactSymbol& dest )
{
std::sentry guard( source );
if ( source ) {
std::string tmp;
std::streambuf* sb = source.rdbuf();
int ch = sb->sgetc();
while ( source && (isalnum( ch ) || ch == '_') ) {
tmp += static_cast< char >( ch );
if ( tmp.size() > sizeof( dest.myName ) ) {
source.setstate( std::ios_base::failbit );
}
}
if ( ch == source::traits_type::eof() ) {
source.setstate( std::ios_base::eofbit );
}
if ( tmp.size() != sizeof( dest.myName ) ) {
source.setstate( std::ios_base::failbit );
}
if ( source ) {
tmp.copy( dest.myName, sizeof( dest.myName ) );
}
}
return source;
}
(Note that unlike some of the other suggestions, for example
using std::istream::read, this one maintains all of the usual
conventions, like skipping leading white space dependent on the
skipws flag.)
Of course, if you can't guarantee 100% that the symbol will
always be 4 characters, you should use std::string for it, and
modify the >> operator accordingly.
And BTW, you seem to want to read four characters into
dcontact, although it's only large enough for three (since
>> will insert a terminating '\0'). If you read any more
than three into it, you have undefined behavior.
try this
int main(int c, char** v) {
string line = "0,1,2,3,4,5,CT_O,6";
char delimiter[7];
int id, ag, lid, cid, fid, did, j = -12345;
char dcontact[5]; // <- The size of <string-field> is known and fixed
stringstream ssline(line);
ssline >> id >> delimiter[0]
>> ag >> delimiter[1]
>> lid >> delimiter[2]
>> cid >> delimiter[3]
>> fid >> delimiter[4]
>> did >> delimiter[5];
ssline.get(dcontact, 5);
ssline >> delimiter[6]
>> j;
std::cout << id << ":" << ag << ":" << lid << ":" << cid << ":" << fid << ":" << did << ":";
std::cout << dcontact << "\n" << j;
}
Since the length of the string is known you can use std::setw(4), as in
ssline >> std::setw(4) >> dcontact >> delimiter[6];