#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 кстати).