Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explaining a string trimming function

Tags:

I came across the code below but need some help with understanding the code. Assume that the string s has spaces either side.

string trim(string const& s){    auto front = find_if_not(begin(s), end(s), isspace);    auto back = find_if_not(rbegin(s), rend(s), isspace);    return string { front, back.base() }; } 

The author stated that back points to the end of the last space whereas the front points to the first non-white space character. So back.base() was called but I don't understand why.

Also what do the curly braces, following string in the return statement, represent?

like image 562
Smithy Avatar asked Jun 26 '14 07:06

Smithy


1 Answers

The braces are the new C++11 initialisation.

.base() and reverse iterators

The .base() is to get back the the underlying iterator (back is a reverse_iterator), to properly construct the new string from a valid range.

A picture. Normal iterator positions of a string (it is a little more complex than this regarding how rend() works, but conceptually anyway...)

        begin                                 end           v                                    v         -------------------------------------         | sp | sp | A | B | C | D | sp | sp |         -------------------------------------       ^                                   ^     rend                                rbegin 

Once your two find loops finish, the result of those iterators in this sequence will be positioned at:

                  front                     v         -------------------------------------         | sp | sp | A | B | C | D | sp | sp |         -------------------------------------                                 ^                               back 

Were we to take just those iterators and construct a sequence from them (which we can't, as they're not matching types, but regardless, supposed we could), the result would be "copy starting at A, stopping at D" but it would not include D in the resulting data.

Enter the back() member of a reverse iterator. It returns a non-reverse iterator of the forward iterator class, that is positioned at the element "next to" the back iterator; i.e.

                  front                     v         -------------------------------------         | sp | sp | A | B | C | D | sp | sp |         -------------------------------------                                     ^                                back.base() 

Now when we copy our range { front, back.base() } we copy starting at A and stopping at the first space (but not including it), thereby including the D we would have missed.

Its actually a slick little piece of code, btw.

Some additional checking

Added some basic checks to the original code.

In trying to keeping with the spirit of the original code (C++1y/C++14 usage), adding some basic checks for empty and white space only strings;

string trim_check(string const& s) {   auto is_space = [](char c) { return isspace(c, locale()); };   auto front = find_if_not(begin(s), end(s), is_space);   auto back = find_if_not(rbegin(s), make_reverse_iterator(front), is_space);   return string { front, back.base() }; } 
like image 130
Niall Avatar answered Sep 20 '22 17:09

Niall