Newbie question on strings

D

Deniz Dogan

Hello.

I am rather new to C/C++ and the only programming/scripting languages
that I know (well) is Java, Ruby, Python and similar high-level
languages. Just so you know where I'm coming from.


I have a really simple problem. I have this piece of code:

// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};

// Checks whether a char is a numerical character (0-9).
bool isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}

int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;
else
y << mystring
}
return 0;
}

As you can see, I'd like to have one string x containing all of the
numerical characters from the input, and all the non-numerical ones in a
string y.

The compilation error that I get is the following:

addressbook.cpp: In function `int main()':
addressbook.cpp:40: error: no match for 'operator<<' in 'x <<
(&mystr)->std::basic_string<_CharT, _Traits, _Alloc>::eek:perator[] [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>](((unsigned int)i))'
addressbook.cpp:47: error: no match for 'operator<<' in 'y <<
(&mystr)->std::basic_string<_CharT, _Traits, _Alloc>::eek:perator[] [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>](((unsigned int)i))'


My guess is that this means that I am disallowed to use the << operator,
for some reason.

Any help is highly appreciated and I apologize for not being able to
solve this problem myself, as it probably is a very simple error.

-- Deniz Dogan
 
D

Deniz Dogan

Deniz said:
Hello.

I am rather new to C/C++ and the only programming/scripting languages
that I know (well) is Java, Ruby, Python and similar high-level
languages. Just so you know where I'm coming from.


I have a really simple problem. I have this piece of code:

// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};

// Checks whether a char is a numerical character (0-9).
bool isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}

int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;
else
y << mystring
}
return 0;
}

As you can see, I'd like to have one string x containing all of the
numerical characters from the input, and all the non-numerical ones in a
string y.

The compilation error that I get is the following:

addressbook.cpp: In function `int main()':
addressbook.cpp:40: error: no match for 'operator<<' in 'x <<
(&mystr)->std::basic_string<_CharT, _Traits, _Alloc>::eek:perator[] [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>](((unsigned int)i))'
addressbook.cpp:47: error: no match for 'operator<<' in 'y <<
(&mystr)->std::basic_string<_CharT, _Traits, _Alloc>::eek:perator[] [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>](((unsigned int)i))'


My guess is that this means that I am disallowed to use the << operator,
for some reason.

Any help is highly appreciated and I apologize for not being able to
solve this problem myself, as it probably is a very simple error.

-- Deniz Dogan


I just realized that sizeof might not be the thing I'm looking for here.
Not sure though.
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

Hello.

I am rather new to C/C++ and the only programming/scripting languages
that I know (well) is Java, Ruby, Python and similar high-level
languages. Just so you know where I'm coming from.

I have a really simple problem. I have this piece of code:

// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};

// Checks whether a char is a numerical character (0-9).
bool isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}

int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";


You don't have to initialize them just 'string x, y;' will do.
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;


To add a character to a string use the += operator.
else
y << mystring
}
return 0;

}
 
D

Deniz Dogan

Erik said:
Hello.

I am rather new to C/C++ and the only programming/scripting languages
that I know (well) is Java, Ruby, Python and similar high-level
languages. Just so you know where I'm coming from.

I have a really simple problem. I have this piece of code:

// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};

// Checks whether a char is a numerical character (0-9).
bool isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}

int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";


You don't have to initialize them just 'string x, y;' will do.
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;


To add a character to a string use the += operator.
else
y << mystring
}
return 0;

}



Thanks a lot, Erik!
 
G

Gavin Deane

Deniz said:
I am rather new to C/C++ and the only programming/scripting languages
that I know (well) is Java, Ruby, Python and similar high-level
languages. Just so you know where I'm coming from.
I have a really simple problem. I have this piece of code:
// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};
// Checks whether a char is a numerical character (0-9).
bool isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}

int main() {
string mystring;
getline(cin, mystring);
string x = "", y = "";
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;
else
y << mystring
}
return 0;
}

As you can see, I'd like to have one string x containing all of the
numerical characters from the input, and all the non-numerical ones in a
string y.
The compilation error that I get is the following:
addressbook.cpp: In function `int main()':
addressbook.cpp:40: error: no match for 'operator<<' in 'x <<
(&mystr)->std::basic_string<_CharT, _Traits, _Alloc>::eek:perator[] [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>](((unsigned int)i))'
addressbook.cpp:47: error: no match for 'operator<<' in 'y <<
(&mystr)->std::basic_string<_CharT, _Traits, _Alloc>::eek:perator[] [with
_CharT = char, _Traits = std::char_traits<char>, _Alloc =
std::allocator<char>](((unsigned int)i))'
My guess is that this means that I am disallowed to use the << operator,
for some reason.


Correct. << in this context is the stream insertion operator. a string
is not a stream so attempting to use the stream insertion operator
makes no sense. Use += to append a char to a string.
I just realized that sizeof might not be the thing I'm looking for here.
Not sure though.

Also correct. sizeof evaluates the size of the object. The size of a
string object is not the same thing as the length of the data the
object is managing. Fortunately, the string class provides you with a
member function, length(), which returns what you need.

Gavin Deane
 
L

Lionel B

Hello.

I am rather new to C/C++ and the only programming/scripting languages
that I know (well) is Java, Ruby, Python and similar high-level
languages. Just so you know where I'm coming from.


I have a really simple problem. I have this piece of code:

// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};

// Checks whether a char is a numerical character (0-9). bool
isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}

int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;
else
y << mystring
}
return 0;
}

As you can see, I'd like to have one string x containing all of the
numerical characters from the input, and all the non-numerical ones in a
string y.


You're probably looking for std::stringstream. Try this:

#include <string>
#include <iostream>
#include <sstream>

using namespace std;

int main()
{
string mystring;
getline(cin, mystring);

stringstream x, y; // does have operator <<

for (string::size_type i = 0; i < mystring.size(); ++i) {
if (isdigit(mystring)) // `std::isdigit' does what your `isnumber' does
x << mystring;
else
y << mystring;
}

cout << "x = " << x.str() << endl; // stringstream::str() returns the actual string
cout << "y = " << y.str() << endl;

return 0;
}
 
D

Daniel T.

Deniz Dogan said:
// Array of "numerical" characters.
char numbers [] = {'0','1','2','3','4','5','6','7','8','9'};

// Checks whether a char is a numerical character (0-9).
bool isnumber(char x) {
for (int i = 0; i < sizeof(numbers); ++i) {
if (x == numbers) {
return true;
}
}
return false;
}


remove all of the above. use the line below instead:

#include said:
int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";
for (int i = 0; i < sizeof(mystring); ++i) {

for ( int i = 0; i < mystring.size(); ++i ) {
if (isnumber(mystring))

if ( isdigit( mystring ) )
x << mystring;

x += mystring;
else
y << mystring y += mystring;
}
return 0;
}
 
O

oleg.belozor

I know, in this place there are many real gurus with huge experience
programming using C++ and I'm interesting, how they can implement this
functionality. My version:

#include <iostream>
#include <conio.h>
#include <sstream>

using namespace std;

int main()
{
char c;
stringstream x, y;
do
{
c = (char)getchar();
isdigit(c) ? x << c : y << c;
}
while(c != '\n');

cout <<"x = " << x.str() << endl;
cout << "y = " << y.str() << endl;

getchar();

return 0;
}

With impatience wait for yours solutions.
 
G

Gavin Deane

I know, in this place there are many real gurus with huge experience
programming using C++ and I'm interesting, how they can implement this
functionality. My version:

#include <iostream>
#include <conio.h>
#include <sstream>

using namespace std;

int main()
{
char c;
stringstream x, y;
do
{
c = (char)getchar();
isdigit(c) ? x << c : y << c;
}
while(c != '\n');

cout <<"x = " << x.str() << endl;
cout << "y = " << y.str() << endl;

getchar();

return 0;

}

With impatience wait for yours solutions.

The main problem with your code as a C++ solution is that conio.h and
getchar are not part of C++. To read and process an entire line, the
getline function, as used by the OP is perfectly adequate. One other
minor point, you only use your string streams for output so x and y
can be ostringstream rather than stringstream.

#include <iostream>
#include <sstream>
#include <string>
#include <cctype>

using namespace std;

int main()
{
string line;
ostringstream x, y;
getline(cin, line);
for (string::size_type i = 0; i < line.length(); ++i)
{
char c = line;
isdigit(c) ? x << c : y << c;
}
cout <<"x = " << x.str() << endl;
cout << "y = " << y.str() << endl;
return 0;
}

Gavin Deane
 
R

red floyd

Gavin said:
The main problem with your code as a C++ solution is that conio.h and
getchar are not part of C++.

Actually, getchar() *is* part of C++ (the stdio library is incorporated
by reference) said:
To read and process an entire line, the
getline function, as used by the OP is perfectly adequate. One other
minor point, you only use your string streams for output so x and y
can be ostringstream rather than stringstream.

#include <iostream>
#include <sstream>
#include <string>
#include <cctype>

using namespace std;

int main()
{
string line;
ostringstream x, y;
getline(cin, line);
for (string::size_type i = 0; i < line.length(); ++i)
{
char c = line;
isdigit(c) ? x << c : y << c;
}
cout <<"x = " << x.str() << endl;
cout << "y = " << y.str() << endl;
return 0;
}

Gavin Deane
 
D

Daniel T.

I know, in this place there are many real gurus with huge experience
programming using C++ and I'm interesting, how they can implement this
functionality. My version:

#include <iostream>
#include <conio.h>
#include <sstream>

using namespace std;

int main()
{
char c;
stringstream x, y;
do
{
c = (char)getchar();
isdigit(c) ? x << c : y << c;
}
while(c != '\n');

cout <<"x = " << x.str() << endl;
cout << "y = " << y.str() << endl;

getchar();

return 0;
}

With impatience wait for yours solutions.

#include <algorithm>
#include <iterator>
#include <iostream>

using namespace std;

bool is_digit( char c )
{
return isdigit( c );
}

int main()
{
string input;
getline( cin, input );
const string::iterator it =
stable_partition( input.begin(), input.end(), &is_digit );
cout << "x = ";
copy( input.begin(), it, ostream_iterator<char>( cout ) );
cout << "\ny = ";
copy( it, input.end(), ostream_iterator<char>( cout ) );
}
 
J

James Kanze

I know, in this place there are many real gurus with huge experience
programming using C++ and I'm interesting, how they can implement this
functionality. My version:
#include <iostream>
#include <conio.h>
#include <sstream>
using namespace std;
int main()
{
char c;
stringstream x, y;

Why stringstream? He's only writing, so at the very least, it
should be ostringstream. And in fact, he's not formatting
anything; he's dealing with elements that are already text. So
just appending to a string would seem just as appropriate.
do
{
c = (char)getchar();

And what happens when you reach end of file?

The standard idiom would be something like:

char c ;
while ( std::cin.get( c ) && c != \=n' ) {
// ...

If it were C, and you only had getchar, then you'd have to
assign the results to an int first, something like:

for ( int c = getchar() ;
c != EOF && c != '\n' ;
c = getchar() ) {
// ...

You can use the same idiom in C++, of course:

for ( int c = std::cin.get() ;
c != EOF && c != '\n' ;
c = std::cin.get() ) {
// ...

In this case, it has a definite advantage, see below:
isdigit(c) ? x << c : y << c;

This has undefined behavior, to start with. You can't call the
one argument isdigit function with a char. If you want to get
fancy, and use ?: (I'm not sure it's the best solution here),
what's wrong with:

( isdigit( static_cast< unsigned char >( c ) )
? x
: y ) += c ;

If c is an int, with the immediate results of std::cin.get(), or
getchar(), then you don't need the cast.
}
while(c != '\n');

And of course, since the tests are at the top of the loop, you
don't need the while here.
cout <<"x = " << x.str() << endl;
cout << "y = " << y.str() << endl;
getchar();

What does this do?
return 0;
}
With impatience wait for yours solutions.

I'd go with std::string for the collectors, my last for loop,
and an if.
 
J

James Kanze

Very good, but...
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;
bool is_digit( char c )
{
return isdigit( c );

Where is isdigit defined. There are isdigit functions defined
in two different standard headers; you didn't include either of
them, and the call here is legal with none of them, it's either:

#include <locale>
// ...
return std::isdigit( c, std::locale() ) ;

or
#include <ctype.h>
// ...
return isdigit( static_cast< unsigned char >( c ) ) ;

Given the way you've written it, why write it at all?
int main()
{
string input;
getline( cin, input );

I'd check that the read succeeded, just to be sure.
const string::iterator it =
stable_partition( input.begin(), input.end(), &is_digit );

In theory, you could use the two argument version of
std::isdigit directly in the call here, by means of ptr_fun and
bind2nd. But since these are all templates, as is the two
argument function, you'd have to explicitly tell the compiler
which instantiation to use. The resulting expression is not
particularly succinct.

If you do this sort of thing for a living, of course, you'll
probably have the necessary predicates already present in your
library, so it would be a lot simpler.
cout << "x = ";
copy( input.begin(), it, ostream_iterator<char>( cout ) );
cout << "\ny = ";
copy( it, input.end(), ostream_iterator<char>( cout ) );

(You forgot the final newline.)

I'd probably write this:

cout << "x = " << std::string( input.begin(), it ) << '\n' ;
cout << "y = " << std::string( it, input.end() ) << '\n' ;

It's probably less efficient this way, but IMHO considerably
more readable.

I do like your use of a standard algorithm.
 
G

Gavin Deane

GavinDeanewrote:

Actually, getchar() *is* part of C++ (the stdio library is incorporated
by reference), but he doesn't include either <stdio.h> or <cstdio>.

My mistake. Thanks. James Kanze's post elsethread seems to cover the
problems in the original use of getchar().

Gavin Deane
 
D

Daniel T.

James Kanze said:
Very good, but...




Where is isdigit defined. There are isdigit functions defined
in two different standard headers; you didn't include either of
them, and the call here is legal with none of them, it's either:

#include <locale>
// ...
return std::isdigit( c, std::locale() ) ;

or
#include <ctype.h>
// ...
return isdigit( static_cast< unsigned char >( c ) ) ;

Given the way you've written it, why write it at all?

The missing include was an oops on my part, apparently it is included
through one of the others on my system. As for the cast to unsigned
char, I feel it is completely unnecessary in this case (I did consider
it.) The only thing at issue is if 'c' is negative and how that will
expand out to an int. If you actually tested isdigit( c ) with isdigit(
static_cast<unsigned char>( c ) ) for all values of 'c', I expect you
will find that the return values are the same in every case. (In looking
around, this may not be true for EBCDIC.)
I'd check that the read succeeded, just to be sure.

I considered it, but didn't find it necessary. If getline fails, input's
value will be unchanged (i.e. empty.) Unless the original requirements
stated that something special had to be done on failure, I see no reason
for it.
In theory, you could use the two argument version of
std::isdigit directly in the call here, by means of ptr_fun and
bind2nd. But since these are all templates, as is the two
argument function, you'd have to explicitly tell the compiler
which instantiation to use. The resulting expression is not
particularly succinct.

I'm interested. I've attempted to do this several times but have been
unable to figure out the syntax. Care to elaborate?
(You forgot the final newline.)

No, I didn't. I found it unnecessary.
I'd probably write this:

cout << "x = " << std::string( input.begin(), it ) << '\n' ;
cout << "y = " << std::string( it, input.end() ) << '\n' ;

It's probably less efficient this way, but IMHO considerably
more readable.

That's a good question. Is it less efficient? I like your use of the
string constructor instead of using copy.
I do like your use of a standard algorithm.

Why reinvent the wheel?
 
J

James Kanze

The missing include was an oops on my part, apparently it is included
through one of the others on my system.

Happens a lot:-(.
As for the cast to unsigned
char, I feel it is completely unnecessary in this case (I did consider
it.) The only thing at issue is if 'c' is negative and how that will
expand out to an int. If you actually tested isdigit( c ) with isdigit(
static_cast<unsigned char>( c ) ) for all values of 'c', I expect you
will find that the return values are the same in every case. (In looking
around, this may not be true for EBCDIC.)

They aren't on any machine I know of (Windows with VC++, Linux
with g++, and Solaris with Sun CC or g++). In all cases,
isalpha(0xFF) returns true (non-zero), but if you assign the
value to a char c, it becomes -1, which is (typically) EOF, and
required to return false. For the other negative characters,
it's possible to implement the function so that they do
systematically return the correct value, but although some
implementations do, it isn't required by the standard, and as
far as I know, it isn't documented or guaranteed by those
implementations. And in the past, I've definitly used
implementations where calling isdigit with a negative value
could return true (although none of the digits are allowed to
have negative values when assigned to a char).
I considered it, but didn't find it necessary. If getline fails, input's
value will be unchanged (i.e. empty.) Unless the original requirements
stated that something special had to be done on failure, I see no reason
for it.

That's a curious attitude. I'd say that unless the original
requirements said that an error should be treated as an empty
string, I see no reason for supposing that it can be treated as
anything but an error.
I'm interested. I've attempted to do this several times but have been
unable to figure out the syntax. Care to elaborate?

Studying it more closely, I'm not sure you can, at least not
without TR1. You can't use std::ctype<>::is, because that is a
member function which requires two arguments, and the adapters
for pointer to member function allow at most one argument. And
you can't use the convenience function std::isdigit, because the
second argument is a reference, and the function adapter doesn't
support reference parameters. (Boost::bind, which is part of
TR1, handles all of these issues, however.)

At one time in the past, I'd posted something similar for
tolower in fr.comp.lang.c++:

typedef std::ctype< char >
Cvt ;
std::transform(
source.begin(), source.end(),
std::back_inserter( dest ),
std::bind1st(
std::mem_fun(
static_cast< char (Cvt::*)( char ) const >(
&Cvt::tolower ) ),
&std::use_facet< Cvt >( std::locale() ) ) ) ;

It was my memories of this which provoked the comment that the
notation wasn't particularly succinct.

As I said, I do this sort of thing often enough to have a
template Is< std::ctype_base::mask mask > (and IsNot) in my
toolkit, so the predicate object would simply be
No, I didn't. I found it unnecessary.

How can you find it unnecessary? It is practically required by
the language. The last character written to a file in text mode
must be a '\n'. (In practice, if the output is redirected to a
file, some programs will be unable to read the partial last
line.)
That's a good question. Is it less efficient?

Probably. You have to construct the std::string objects, which
typically requires a dynamic allocation. I doubt that it would
be measurable compared to the actual output, however.
I like your use of the
string constructor instead of using copy.

In this particular case, the motivation is to not have to
interrupt the chaining of the << to output the data.
 
D

Daniel T.

On May 26, 5:23 p.m., "Daniel T." <[email protected]> wrote:
That's a curious attitude. I'd say that unless the original
requirements said that an error should be treated as an empty
string, I see no reason for supposing that it can be treated as
anything but an error.

Of the two reference programs given, one went into an infinite loop if
getchar failed, while the other did exactly what my program does. I
chose to go with the more reasonable reference.
How can you find it unnecessary? It is practically required by
the language. The last character written to a file in text mode
must be a '\n'. (In practice, if the output is redirected to a
file, some programs will be unable to read the partial last
line.)

Required by the language? Maybe that is too strong of a phrase...
"Some programs" are written incorrectly.
 
J

James Kanze

Required by the language? Maybe that is too strong of a phrase...

Well, it says in the C standard that an implementation is not
required to process a text file correctly if it doesn't end with
a '\n', and the C++ standard defines its behavior here by
reference to the C standard. Not terminating a text file with a
'\n' is an error.
"Some programs" are written incorrectly.

Yes: those that don't terminate a text file with a '\n'.

Note that even when the output is to a window running a command
interpreter, your last line will run into the prompt if it isn't
correctly terminated. If you're prompt begins with a new line,
you won't be bothered, but that results in a lot of extra space
when you use programs which do work correctly.
 
D

Daniel T.

James Kanze said:
Well, it says in the C standard that an implementation is not
required to process a text file correctly if it doesn't end with
a '\n', and the C++ standard defines its behavior here by
reference to the C standard. Not terminating a text file with a
'\n' is an error.

So if my program is supposed to output something that must be processed
by a C/C++ implementation, then I need a newline. That says nothing
about the program I wrote.

However, given the fact that one of the reference programs mentioned
ended with a newline (the other didn't produce any output at all,) I
should probably have done so as well. Thus, using the same argument I
used to show that a blank string on bad input is correct, I show that
not putting a newline at the end is incorrect. :)
 
J

Jerry Coffin

[ ... ]
int main() {
string mystring;
getline(cin, mystring);

string x = "", y = "";
for (int i = 0; i < sizeof(mystring); ++i) {
if (isnumber(mystring))
x << mystring;
else
y << mystring
}
return 0;
}


Yet another approach looks like this:

#include <algorithm>
#include <string>
#include <cctype>

struct non_digit {
bool operator()(int d) {
return !std::isdigit(d);
}
};

int main() {
using namespace std;

string mystring, x, y;

getline(cin, mystring);
remove_copy_if(mystring.begin(), mystring.end(),
back_inserter(x), non_digit());
remove_copy_if(mystring.begin(), mystring.end(),
back_inserter(y), isdigit);

return 0;
}

This has the disadvantage of scanning through the input string twice
though, so it's at least theoretically slower. OTOH, given that the
string is presumably being entered by the user, it's unlikely to be long
enough for this to mean much.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top