Use iterators to identify the portion of the string you want to remove, and the
erase
member function to remove it. Example 4-2 presents the function rtrim
that trims a character from the end of a string.
Example 4-2. Trimming characters from a string
#include <string> #include <iostream> // The approach for narrow character strings void rtrim(std::string& s, char c) { if (s.empty()) return; std::string::iterator p; for (p = s.end(); p != s.begin() && *--p == c;); if (*p != c) p++; s.erase(p, s.end()); } int main() { std::string s = "zoo"; rtrim(s, 'o'); std::cout << s << '\n'; }
Example 4-2 will do the trick for
strings of char
s, but it only
works for char
strings. Just like you saw in Example 4-1, you can take advantage of the
generic design of basic_string
and use a function
template
instead. Example 4-3 uses a
function template to trim characters from the end of any kind of character string.
Example 4-3. A generic version of rtrim
#include <string> #include <iostream> using namespace std; // The generic approach for trimming single // characters from a string template<typename T> void rtrim(basic_string<T>& s, T c) { if (s.empty()) return; typename basic_string<T>::iterator p; for (p = s.end(); p != s.begin() && *--p == c;); if (*p != c) p++; s.erase(p, s.end()); } int main() { string s = "Great!!!!"; wstring ws = L"Super!!!!"; rtrim(s, '!'); rtrim(ws, L'!'); cout << s << '\n'; wcout << ws << L'\n'; }
This function works exactly the same way as the previous, nongeneric, version in Example 4-2, but since it is parameterized on
the type of character being used, it will work for basic_string
s of any kind.
Examples Example 4-2 and Example 4-3 remove sequences of a single
character from a string. Trimming whitespace is different, however, because whitespace can
be one of several characters. Conveniently, the standard library provides a concise way to
do this: the isspace
function in the <cctype>
header (and its wchar_t
equivalent, iswspace
, in <cwctype>
). Example 4-4 defines a generic function that trims trailing whitespace.
Example 4-4. Trim trailing whitespace
#include <string> #include <iostream> #include <cctype> #include <cwctype> using namespace std; template<typename T, typename F> void rtrimws(basic_string<T>& s, F f) { if (s.empty()) return; typename basic_string<T>::iterator p; for (p = s.end(); p != s.begin() && f(*--p);); if (!f(*p)) p++; s.erase(p, s.end()); } // Overloads to make cleaner calling for client code void rtrimws(string& s) { rtrimws(s, isspace); } void rtrimws(wstring& ws) { rtrimws(ws, iswspace); } int main() { string s = "zing "; wstring ws = L"zong "; rtrimws(s); rtrimws(ws); cout << s << "|\n"; wcout << ws << L"|\n"; }
The function template in Example
4-4,
rtrimws
, is a generic function template, similar to the
previous examples, that accepts a basic_string
and
trims whitespace from the end of it. But unlike the other examples, it takes a function
object, and not a character, that is used to test an element of the string to determine
whether it should be removed.
You don’t need to overload rtrimws
as I did in the
example, but it makes the syntax cleaner when using the function, since the calling code
can omit the predicate function argument when using them.
But alas, this solution requires that you write the code yourself. If you would rather use a library—and a good one at that—Boost’s String Algorithms library supplies lots of functions for trimming strings, and chances are that what you need is already there. In fact, there are lots of handy trimming functions in the String Algorithms library, so if you can use Boost you should take a look. Table 4-1 lists the function templates in the library that you can use for trimming strings, including some miscellaneous functions. Since these are function templates, they have template parameters that represent the different types used. Here is what each of them mean:
- Seq
This is a type that satisfies the sequence requirements as defined in the C++ standard.
- Coll
This is a type that satisfies a less-restrictive set of requirements than a standard sequence. See the Boost String Algorithms definitions a detailed description of the requirements a collection satisfies.
- Pred
This is a function object or function pointer that takes a single argument and returns a bool—in other words, an unary predicate. You can supply your own unary predicates to some of the trimming functions to trim elements that satisfy certain criteria.
- OutIt
This is a type that satisfies the requirements of an output iterator as defined in the C++ standard, namely that you can increment it and assign to the new location to add an element to the end of the sequence to which it points.
Table 4-1. Boost’s string trimming function templates
The first four function templates described in Table 4-1 are the core functionality of the String Algorithms library’s trim functions. The rest are variations on those themes. To see some of them in action, take a look at Example 4-5. It shows some of the advantages of using these functions over string member functions.
Example 4-5. Using Boost’s string trim functions
#include <iostream> #include <string> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main() { string s1 = " leading spaces?"; trim_left(s1); // Trim the original string s2 = trim_left_copy(s1); // Trim, but leave original intact cout << "s1 = " << s1 << endl; cout << "s2 = " << s2 << endl; s1 = "YYYYboostXXX"; s2 = trim_copy_if(s1, is_any_of("XY")); // Use a predicate to trim_if(s1, is_any_of("XY")); cout << "s1 = " << s1 << endl; cout << "s2 = " << s2 << endl; s1 = "1234 numbers 9876"; s2 = trim_copy_if(s1, is_digit()); cout << "s1 = " << s1 << endl; cout << "s2 = " << s2 << endl; // Nest calls to trim functions if you like s1 = " ****Trim!*** "; s2 = trim_copy_if(trim_copy(s1), is_any_of("*")); cout << "s1 = " << s1 << endl; cout << "s2 = " << s2 << endl; }
Example 4-5 demonstrates how to use
the Boost string trim functions. They are generally self-explanatory to use, so I won’t go
into a detailed explanation beyond what’s in Table 4-1. The one function that is in the example that isn’t in the table is
is_any_of
. This is a function template that returns a
predicate function object that can be used by the trim_if
-style functions. Use it when you want to trim a set of characters.
There is a similar classification function named is_from_range
that takes two arguments and returns an unary predicate that
returns true when a character is within the range. For example, to trim the characters
a
through d
from a
string, you could do something like this:
s1 = "abcdXXXabcd"; trim_if(s1, is_from_range('a', 'd')); cout << "s1 = " << s1 << endl; // Now s1 = XXX
Note that this works in a case-sensitive way, since the range a
through d
does not include the uppercase
versions of those
letters.
Get C++ Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.