Remove suffix from string

S

Spoon

Hello,

I'm writing code to remove .whatever from the end of a string.

e.g. hello.world.foo.bar -> hello.world.foo

The following code seems to work

#include <string>
#include <iostream>
int main(int argc, char **argv)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.'));
std::cout << foo << std::endl;
return 0;
}

$ ./a.out hello.world.foo.bar
hello.world.foo

but it crashes when rfind() returns npos

$ ./a.out hello+world+foo+bar
terminate called after throwing an instance of 'std::eek:ut_of_range'
what(): basic_string::erase
Aborted

Is pos+1 valid, even when pos==npos?

If so, I could write

#include <string>
#include <iostream>
int main(int argc, char **argv)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.')+1);
std::cout << foo << std::endl;
return 0;
}

Then I'd have to remove the trailing '.' and the entire string is erased
if there is no '.' (which may be acceptable).

Is there a better solution?

Regards.
 
K

Kai-Uwe Bux

Spoon said:
Hello,

I'm writing code to remove .whatever from the end of a string.

e.g. hello.world.foo.bar -> hello.world.foo

The following code seems to work

#include <string>
#include <iostream>
int main(int argc, char **argv)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.'));
std::cout << foo << std::endl;
return 0;
}

$ ./a.out hello.world.foo.bar
hello.world.foo

but it crashes when rfind() returns npos

$ ./a.out hello+world+foo+bar
terminate called after throwing an instance of 'std::eek:ut_of_range'
what(): basic_string::erase
Aborted

Is pos+1 valid, even when pos==npos?

Yes: the result is of type string::size_type which is unsigned. Unsigned
built-in arithmetic types wrap on overflow.

However, since you have to ask, it relying on it may not be a good idea from
a maintenance point of view: the programmer that comes after you might not
know and get confused (notice that this could easily be yourself a few
months down the road).
If so, I could write

#include <string>
#include <iostream>
int main(int argc, char **argv)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.')+1);
std::cout << foo << std::endl;
return 0;
}

Then I'd have to remove the trailing '.' and the entire string is erased
if there is no '.' (which may be acceptable).

Is there a better solution?

What about

foo.erase( std::min( foo.rfind('.'), foo.size() ) );


Best

Kai-Uwe Bux
 
S

Salt_Peter

Spoon said:
Hello,

I'm writing code to remove .whatever from the end of a string.

e.g. hello.world.foo.bar -> hello.world.foo

The following code seems to work

#include <string>
#include <iostream>
int main(int argc, char **argv)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.'));

instead:
foo.assign(foo, 0, foo.rfind('.'));
std::cout << foo << std::endl;
return 0;
}

$ ./a.out hello.world.foo.bar
hello.world.foo

but it crashes when rfind() returns npos

did you supply argv[1]? How about a check with argc in the code.
also, erase(...) expects iterators. assign(...) takes a size_type.
consult the header.
$ ./a.out hello+world+foo+bar
terminate called after throwing an instance of 'std::eek:ut_of_range'
what(): basic_string::erase
Aborted

Is pos+1 valid, even when pos==npos?

If so, I could write

#include <string>
#include <iostream>
int main(int argc, char **argv)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.')+1);
std::cout << foo << std::endl;
return 0;
}

Then I'd have to remove the trailing '.' and the entire string is erased
if there is no '.' (which may be acceptable).

Is there a better solution?

Regards.
 
S

Spoon

Salt_Peter said:
Spoon said:
$ ./a.out hello.world.foo.bar
hello.world.foo

but it crashes when rfind() returns npos

did you supply argv[1]?

Errr... yes. (I provided the command and the output)
How about a check with argc in the code.

This is but a toy example.
also, erase(...) expects iterators.

This statement is inaccurate, as far as I can tell.

A valid prototype for erase is

basic_string& erase(size_type off = 0, size_type count = npos);
assign(...) takes a size_type.
consult the header.

I will.

Regards.
 
S

Salt_Peter

Spoon said:
Salt_Peter said:
Spoon said:
$ ./a.out hello.world.foo.bar
hello.world.foo

but it crashes when rfind() returns npos

did you supply argv[1]?

Errr... yes. (I provided the command and the output)

Ah, you need to run the program otherwise the arguement isn't injected.
I checked the header... and... you're RIGHT. erase *does* have a
version with size_type.

Try:

#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
if(argc > 1)
{
std::string foo(argv[1]);
foo.erase(foo.rfind('.'));
std::cout << foo << std::endl;
} else {
std::cout << "argc = " << argc;
std::cout << std::endl;
}
return 0;
}

If you see the argc = # ouput then run the program instead. I got the
following with an arguement of test.txt
/*
test
*/
and the following when debugging:
/*
argc = 1
*/

Please accept my sincere apologees for misleading you.
 
P

Pete Becker

Spoon said:
Is there a better solution?

inline void erase_end(std::string& str, std::string::size_type pos)
{
if (pos != std::string::npos)
erase(str, pos);
}

--

-- Pete

Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." For more information about this book, see
www.petebecker.com/tr1book.
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top