Re: Newbie question on strings
On May 26, 5:23 pm, "Daniel T." <danie...@earthlink.net> wrote:
James Kanze <james.ka...@gmail.com> wrote:
"Daniel T." <danie...@earthlink.net> wrote:
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?
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).
}
int main()
{
string input;
getline( cin, input );
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.
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.
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.
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
Is<std::ctype_base::digit>(). (You can pass a locale to the
constructor, but by default, it uses the current global locale.)
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.)
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.)
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?
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.
--
James Kanze (Gabi Software) email: james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34