Объяснение функции обрезки строк

#c #string #c 11 #c 14

#c #строка #c 11 #c 14

Вопрос:

Я наткнулся на приведенный ниже код, но мне нужна помощь в понимании кода. Предположим, что строка s имеет пробелы с обеих сторон.

 string trim(string constamp; 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() };
}
  

Автор заявил, что задняя часть указывает на конец последнего пробела, тогда как передняя часть указывает на первый символ, не являющийся пробелом. Итак, был вызван back.base(), но я не понимаю почему.

Кроме того, что представляют фигурные скобки, следующие за строкой в операторе return?

Комментарии:

1. Если не считать профилирования всего этого, это на самом деле выглядит как аккуратный фрагмент кода.

2. Этот код приведет к сбою на входных данных, состоящих только из одного или нескольких символов пробела, поскольку итераторы будут пересекаться.

3. @j_random_hacker я согласен. для обеспечения большего значения требуется предохранительный клапан back.base() front , иначе просто верните пустую строку. Мне все еще нравится идея в принципе. Я думаю, что это фактически вызовет исключение длины, потому что результат last first будет отрицательным.

Ответ №1:

Фигурные скобки — это новая инициализация C 11.

.base() и обратные итераторы

Задача .base() состоит в том, чтобы вернуть базовый итератор ( back is a reverse_iterator ), чтобы правильно построить новую строку из допустимого диапазона.

Картинка. Обычные позиции итератора строки (это немного сложнее, чем это, в отношении того, как rend() работает, но концептуально в любом случае …)

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

Как только ваши два цикла поиска завершатся, результат этих итераторов в этой последовательности будет помещен в:

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

Если бы мы взяли только эти итераторы и построили из них последовательность (чего мы не можем, поскольку они не соответствуют типам, но, несмотря на это, предполагали, что мы могли бы), результатом было бы «копировать, начиная с A, останавливаясь на D», но это не включало бы D в результирующие данные.

Введите back() элемент обратного итератора. Он возвращает необратный итератор класса прямого итератора, который расположен в элементе «рядом с» обратным итератором; т.е.

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

Теперь, когда мы копируем наш диапазон { front, back.base() } , мы копируем, начиная с A и заканчивая первым пробелом (но не включая его), тем самым включая D, который мы бы пропустили.

На самом деле это маленький кусочек кода, кстати.

Некоторые дополнительные проверки

Добавлены некоторые базовые проверки в исходный код.

В попытке сохранить дух исходного кода (использование C 1y / C 14), добавив некоторые базовые проверки на наличие пустых строк и пробелов;

 string trim_check(string constamp; 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() };
}
  

Комментарии:

1. Стоит отметить base() , что итератор ссылается на элемент рядом с элементом, на который ссылается обратный итератор. В этом случае несколько синонимично std::next(back) , но не в обратном направлении, а в «прямом» направлении базовой последовательности.

2. @WhozCraig Да. В качестве дополнительной ссылки en.cppreference.com/w/cpp/iterator/reverse_iterator содержит хорошую запись о reverse_iterator .

3. Спасибо за ваши комментарии, но почему front указывает на первый символ, не содержащий пробелов, а back не указывает на последний символ, не содержащий пробелов?

4.@Smithy back ссылается на последний символ без пробелов. Но если вы включите это в качестве end итератора копии (или, в вашем случае, конструктора диапазона итераторов), это позиция остановки, и вам не хватает одного слота (и в любом случае это итератор неправильного типа). Вы не хотите останавливаться на достигнутом, вы хотите остановить один «слот» после этой позиции. Подумайте об этом аналогично тому, как end() в обычном итераторе последовательности ссылаются на «один-последний» последний элемент. Помните, что в C конечные точки итератора означают остановку, когда вы «добираетесь сюда», а не остановку, когда вы «проходите здесь». Я надеюсь, что это имело смысл.

5. Моя ascii-графика довольно хромает, но я надеюсь, что это дает представление об этом ответе (= 1 кстати).