В настоящее время я использую следующий код для обрезки всего std::strings в моих программах:
std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);
Он работает нормально, но мне интересно, есть ли какие-то крайние случаи, когда он может выйти из строя?
Конечно, приветствуются ответы с элегантными альтернативами, а также левостороннее решение.
@IdanK И в C++ 11 до сих пор нет этой функции.
@IdanK: Отлично, не правда ли! Посмотрите на все конкурирующие варианты, которые теперь есть в нашем распоряжении, не обремененные идеей одного человека о «в способе, которым мы должны это сделать»!
@LightnessRacesinOrbit: наличие удобных функций в библиотеке не исключает возможности делать что-то по-другому. В библиотеке есть vector, но мы не обязаны использовать только вектор.
@MooingDuck: Выбор типов - это одно; выбор функциональности в пределах данного типа является другим, поскольку предоставление этих вариантов может создать ограничения для остальной части типа и его реализации.
Функциональность @LightnessRacesinOrbit внутри типа, ну, это дизайнерское решение, и добавление функции обрезки к строке может (по крайней мере, в C++) в любом случае быть не лучшим решением, но не предоставляет какой-либо стандартный способ сделать это, вместо этого позволяя всем беспокоиться одни и те же такие мелкие проблемы снова и снова, конечно, тоже никому не помогают
Я действительно не знаю, о чем ты. является - стандартный способ делать эти вещи в C++: путем включения специализированной библиотеки по вашему выбору и использования функции обрезки это вместе со всеми другими специализированными функциями для вашей проблемной области.
@DevSolar, для этого нет способа стандарт, потому что std :: string в библиотеке стандарт ("std" означает стандарт FYI) не предоставляет этого. Вы должны использовать стороннюю библиотеку.
@ MilanBabuškov: Такие языки, как Java или C#, пытаются предоставить библиотеки «все поет, все танцуют» как часть языка. Такие языки, как C или C++, предоставляют только базовые знания, и предоставляют специалистам право придумывать решения для предметной области. Это их «стандартный» подход. Я являюсь сопровождающим проприетарной библиотеки C++, которая выполняет токенизацию, обрезку, анализ, сравнение и дедупликацию международных адресов, и я могу сказать вам, что наличие trim() в std:: не спасло бы меня от работы, поскольку моя проблемная область требовала, чтобы я работал в первую очередь со строками, не относящимися к std:: / STL.
Вы можете спросить, почему функции обрезки не встроены в класс std::string, когда именно такие функции делают другие языки такими удобными в использовании (например, Python).
@IdanK, по крайней мере, один влиятельный гуру C++ считает, что в стандартном строковом классе уже есть слишком много. Не уверен, что согласен, но это интересное чтение: gotw.ca/gotw/084.htm
@MarkRansom, да, есть аргументы, почему это не должен быть в строковый класс, но это может быть бесплатная функция, например std :: trim (...), как в boost.
Слишком много того, что, по мнению Степанова, должно быть в STL, чтобы сделать его универсальным, и слишком мало того, что потребности должно быть в STL, чтобы сделать его полезным; пример разрыва в понимании и того, что происходит, когда что-то разрабатывается на основе теории, а не требований. (но я все время им пользуюсь :)
Нет проблем, я напишу свою 1.000.000-ю версию. Использование виртуального интерфейса trimmable и его упаковка в полдюжины слоев хороших общих шаблонов, так что вы также можете обрезать обезьян и жирафов (возможно, на месте и / или с помощью специального распределителя) практически без накладных расходов на кодирование и молниеносной оптимизации.
1. Что-то вроде буста должен быть новый новый STL. 2. В C++ должны быть директивы условной компиляции (например, weak, но встроенные в компилятор). И тогда C++, наконец, сможет снова соревноваться на равных с другими языками как в удобстве использования, так и в переносимости.
Левая отделка: s = s.substr(s.find_first_not_of(" "));
Мне кажется, что однострочный ответ, предоставленный вопрошающим, лучше всех, если не все, из подробных ответов, представленных ниже.
Есть одна вещь, которая раздражает меня в C++, даже когда я приобрел опыт. Если ожидается импорт других библиотек для более высоких уровней абстракции, почему в стандарте не указаны модули? Мне потребовалось 6 лет, чтобы преодолеть многочисленные препятствия и, наконец, скомпилировать, связать и включить внешние библиотеки, не полагаясь на (часто устаревшие) руководства по быстрому запуску.
Для всех, кто работает в мире .Net, вот хорошая статья о тестировании некоторых из самых быстрых способов обрезки строк там: Самый быстрый способ обрезать строки в C# .Net
Да здравствует Java, API лучший.
Этот тип вопросов показывает как лучшее, так и худшее в C++.
По-прежнему нет поддержки ltrim и rtrim в C++ 17 ?!
Я начал писать код на C++ в 2014 году. Вскоре перешел на JS, Java и PHP. Возвращаясь к C++ пятью годами позже, прискорбно видеть, насколько мало улучшилась стандартная библиотека.
Кто-нибудь знает, почему у C++ есть эти основные проблемы? По сравнению с C# работа со строкой в C++ невероятно убогая. Я не могу поверить, что в C++ каждый должен писать свою функцию для строки Trim, TrimStart, TrimEnd, Split, Join, ....
@bmi QtCore - хорошая альтернатива стандартной библиотеке для строк, контейнеров и т.д. Хорошая сторона в том, что Qt имеет целую экосистему библиотек, плохая сторона в том, что это не стандарт (снижает совместимость). Стандартная библиотека реализует концепции очень низкого уровня и имеет неудобный дизайн.





В случае пустой строки ваш код предполагает, что добавление 1 к string::npos дает 0. string::npos относится к беззнаковому типу string::size_type. Таким образом, вы полагаетесь на поведение сложения при переполнении.
Вы формулируете это так, как будто это плохо. Подписано целочисленное поведение переполнения плохое.
Добавление 1 к std::string::nposдолжен дает 0 согласно C++ Standard. Так что это хорошее предположение, на которое можно полностью положиться.
Проще всего было бы использовать Строковые алгоритмы Boost:
#include <boost/algorithm/string.hpp>
std::string str("hello world! ");
boost::trim_right(str);
str теперь "hello world!". Также есть trim_left и trim, которые обрезаны с обеих сторон.
Если вы добавите суффикс _copy к любому из названий вышеупомянутых функций, например. trim_copy, функция вернет обрезанную копию строки вместо ее изменения посредством ссылки.
Если вы добавите суффикс _if к любому из названий вышеупомянутых функций, например. trim_copy_if, вы можете обрезать все символы, удовлетворяющие вашему пользовательскому предикату, а не только пробелы.
Что использует boost, чтобы определить, является ли символ пробелом?
Это зависит от региона. Моя локаль по умолчанию (VS2005, en) означает, что табуляция, пробелы, возврат каретки, перевод строки, вертикальные табуляции и подача формы обрезаны.
Я уже использую много ускорения, #include <boost/format.hpp> #include <boost/tokenizer.hpp> #include <boost/lexical_cast.hpp>, но беспокоился о раздувании кода для добавления в <boost/algorithm/string.hpp>, когда уже есть альтернативы на основе std::string::erase. С удовольствием сообщаю при сравнении сборок MinSizeRel до и после его добавления, что эта обрезка ускорения вообще не увеличила мой размер кода (должно быть, уже где-то за это платит), и мой код не загроможден еще несколькими функциями.
@MattyT: Какую ссылку вы используете для этого списка (определяя, является ли символ пробелом)?
Boost - такой мощный молоток для такой крошечной проблемы.
@rodarmor: Boost решает множество мелких проблем. Это огромный молоток, который многое решает.
Boost - это набор молотков разных размеров, решающий множество различных задач.
Boost - это большой швейцарский армейский нож, который нужно толкать и толкать, чтобы он поместился в кармане.
Единственное, что является практичным для IMO, - это предоставление контента для стандартной библиотеки (идеи, которые добавляются в результате повышения).
@Ibrahim Boost - это универсальный трансконтинентальный завод по производству инструментов обсудить.joelonsoftware.com/default.asp?joel.3.219431.12&
@rodarmor Вы говорите, что Boost - это монолит по принципу «все или ничего», где включение одного из его заголовков каким-то образом накладывает все на свою программу. Что явно не так. Кстати, Boost никогда не использовал, черт возьми.
@underscore_d но это так. Он никогда не включает «только один файл». В итоге вы включаете больше исходного кода из boost, чем из вашего собственного проекта.
Несколько очень быстрых тестов на моей конкретной машине с моей конкретной строкой говорят мне, что ускорение требует чуть более чем в два раза больше времени для его обрезки, чем решение Эвана, приведенное выше.
на самом деле не отвечает на вопрос, который запрашивает std :: string (не для boost или любой другой библиотеки ...)
Я не уверен, что ваша среда такая же, но в моем случае пустая строка приведет к прерыванию программы. Я бы либо обернул этот вызов стирания с помощью if (! S.empty ()), либо использовал Boost, как уже упоминалось.
Взломал Cplusplus.com
std::string choppa(const std::string &t, const std::string &ws)
{
std::string str = t;
size_t found;
found = str.find_last_not_of(ws);
if (found != std::string::npos)
str.erase(found+1);
else
str.clear(); // str is all whitespace
return str;
}
Это также работает для нулевого случая. :-)
Это просто rtrim, а не ltrim
^ вы не против использовать find_first_not_of? Его относительно легко изменить.
РЕДАКТИРОВАТЬ Начиная с C++ 17, некоторые части стандартной библиотеки были удалены. К счастью, начиная с C++ 11, у нас есть лямбда-выражения, которые являются лучшим решением.
#include <algorithm>
#include <cctype>
#include <locale>
// trim from start (in place)
static inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if (s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string &s) {
s.erase(std::find_if (s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
// trim from both ends (in place)
static inline void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
ltrim(s);
return s;
}
// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
rtrim(s);
return s;
}
// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
trim(s);
return s;
}
Спасибо https://stackoverflow.com/a/44973498/524503 за создание современного решения.
Для стрижки я обычно использую один из этих трех:
#include <algorithm>
#include <functional>
#include <cctype>
#include <locale>
// trim from start
static inline std::string <rim(std::string &s) {
s.erase(s.begin(), std::find_if (s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
// trim from end
static inline std::string &rtrim(std::string &s) {
s.erase(std::find_if (s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
// trim from both ends
static inline std::string &trim(std::string &s) {
return ltrim(rtrim(s));
}
Они говорят сами за себя и очень хорошо работают.
РЕДАКТИРОВАТЬ: Кстати, у меня есть std::ptr_fun, чтобы помочь устранить неоднозначность std::isspace, потому что на самом деле есть второе определение, которое поддерживает локали. Это могло быть все то же самое, но мне это нравится больше.
РЕДАКТИРОВАТЬ: для ответа на некоторые комментарии о принятии параметра по ссылке, изменении и возврате его. Я согласен. Реализация, которую я, скорее всего, предпочел бы, состояла бы из двух наборов функций, один для на месте, а другой для создания копии. Лучшим набором примеров было бы:
#include <algorithm>
#include <functional>
#include <cctype>
#include <locale>
// trim from start (in place)
static inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if (s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
}
// trim from end (in place)
static inline void rtrim(std::string &s) {
s.erase(std::find_if (s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}
// trim from both ends (in place)
static inline void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
ltrim(s);
return s;
}
// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
rtrim(s);
return s;
}
// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
trim(s);
return s;
}
Я сохраняю исходный ответ выше, хотя для контекста и в интересах сохранения доступного ответа с высоким рейтингом.
Этот код не работал с некоторыми международными строками (в моем случае shift-jis хранится в std :: string); Я решил использовать boost::trim для решения проблемы.
Я бы использовал указатели вместо ссылок, чтобы из точки вызова было намного легче понять, что эти функции редактируют строку на месте, а не создают копию.
Обратите внимание, что с isspace вы можете легко получить неопределенное поведение с символами, отличными от ASCII stacked-crooked.com/view?id=49bf8b0759f0dd36dffdad47663ac69f
@ R.MartinhoFernandes, правда, решение, которое я использовал в прошлом, - это шаблон: template<int (&F)(int)> int safe_ctype(unsigned char c) { return F(c); }, который можно использовать так: safe_ctype<std::isspace>(ch);
Почему статика? Здесь предпочтительнее анонимное пространство имен?
@TrevorHickey, конечно, вы можете использовать анонимное пространство имен вместо этого, если хотите.
Если это не компилируется для вас или вы согласны с идеей, что «строка кода не является абстракцией», см. моя суть реализации, который реализует эти функции с использованием только итераторов.
Для согласованности имен с std::string я бы рекомендовал имена trim_front и trim_back.
Еще одно предложение - сделать это универсальным и поддерживать различные формы std::basic_string. <br /> template<class Ch, class Tr, class Al>inline void ltrim(std::basic_string<Ch, Tr, Al>& s) {...}
См. мой ответ ниже для поддержки более широкого диапазона обрезанных символов.
Возвращение и с изменением аргумента: Плохо! :-)
@JohannesOvermann, я в целом согласен. Намерение состояло в том, чтобы упростить объединение этой функции с другими вызовами. Если бы я переписал это сегодня. Я бы сделал trim (который выполняет работу на месте) и trimmed, который возвращал бы копию обрезанной строки.
@JohannesOvermann, я добавил правку к этому ответу, чтобы решить ваши (и некоторые другие) проблемы.
@EvanTeran AFAICT идиоматический современный способ C++ - возвращать по значению, а не изменять ссылку. В любом случае проблема Йоханнеса заключалась в том, что вы оба принимали неконстантный ссылочный аргумент и изменяли его на месте, и возвращал ту же ссылку на тот же аргумент. Какая в этом цель? Мне кажется, что он в конечном итоге будет вызываться в ситуациях, когда он выглядит возвращается как новая строка и, следовательно, как будто входная строка не изменяется - но это было все время - что похоже на рецепт путаницы и / или катастрофа.
@underscore_d посмотрите внимательно, текущая версия этого поста имеет две версии: те, которые принимают по ссылке, модифицируют на месте и ничего не возвращают. Те, которые заканчиваются на «ed» (копирующие). Возьмите параметр копировать, измените его и верните по значению. Из-за семантики перемещения С ++ 11 «копирующие» версии будут выполнять минимальное количество копий. Что касается того, "какова цель этого", я уже ответил на это в своем предыдущем комментарии. По сути, это была «цепочка вызовов».
std :: ptr_fun больше не рекомендуется
@johnbakers правда, можно было бы использовать приведение или лямбду в более современном коде.
До сих пор не понимаю, почему у него так много голосов. Использовать find_last_not_of просто и понятно!
в rtime, какова цель базового вызова find_if ??
std :: ptr_fun устарел в C++ 17. Чем его заменить?
@ user3717478: Перекрестная публикация на свой вопрос по теме: stackoverflow.com/a/44973511/560648. Он объявлен устаревшим в C++ 11; удаленный в C++ 17.
С C++ 17, вероятно, будет лучше использовать std::string_view.
@WorldSEnder Согласен. Я только что сделал свою версию, и она работает в 4 раза быстрее на 1000000 строках, чем принятый ответ. Я использую ручной цикл и настраиваемую функцию is_space, которая использует случай переключения.
@tom Замените int ch на unsigned char ch в закрытии, и это не вызовет сбой утверждения.
Обратите внимание, что в зависимости от реализации обрезка строки справа перед обрезкой слева может быть быстрее, чем наоборот.
@Tom: Shift-JIS не является «международным», если вы живете в стране, которая его использует. Вы имеете в виду не-ASCII.
@SagarKumar Правки, улучшающие существующие ответы, приветствуются, но полностью изменить чей-то код на что-то принципиально иное абсолютно неприемлемо, особенно если он объективно хуже, и удаляет много информации. Если вы считаете, что это того стоит, вы всегда можете добавить свой ответ.
Поведение передачи «int ch» в «isspace» не определено, замените «int ch» на «unsigned char ch». [] (беззнаковый символ ch) {возврат! std :: isspace (ch); } см .: en.cppreference.com/w/cpp/string/byte/isspace
Используйте следующий код для обрезки справа (завершающих) пробелов и символов табуляции из std::strings (идеона):
// trim trailing spaces
size_t endpos = str.find_last_not_of(" \t");
size_t startpos = str.find_first_not_of(" \t");
if ( std::string::npos != endpos )
{
str = str.substr( 0, endpos+1 );
str = str.substr( startpos );
}
else {
str.erase(std::remove(std::begin(str), std::end(str), ' '), std::end(str));
}
И чтобы сбалансировать ситуацию, я также включу левый код обрезки (идеона):
// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if ( string::npos != startpos )
{
str = str.substr( startpos );
}
Это не обнаружит другие формы пробелов ... в частности, новую строку, перевод строки, возврат каретки.
Верно. Вы должны настроить его для пробелов, которые вы хотите обрезать. Мое конкретное приложение ожидало только пробелов и вкладок, но вы можете добавить \ n \ r, чтобы поймать другие.
str.substr(...).swap(str) лучше. Сохраните задание.@updogliu Не будет ли он использовать назначение перемещения basic_string& operator= (basic_string&& str) noexcept;?
Этот ответ не изменяет строки, состоящие из ВСЕХ пробелов. Это провал.
^ Прежде чем оставлять комментарии, убедитесь, что вы пытаетесь ответить на опубликованный ответ. @Tom, чтобы включить все это, убедитесь, что вы включаете их в вызовы функции first / last_not_of.
@TomAndersen, проверьте условие, при котором string::npos != startpos не работает, что означает оператор else для вышеуказанных if. Внутри него обрезайте все пробелы, используя идиому «стереть-удалить». Вот пример: ideone.com/eelaVO
@BilltheLizard, вы можете включить в ответ оператор else, чтобы он мог принести пользу другим.
Для правой обрезки ... почему бы не использовать простой resize()? Скорее всего, это будет просто единственное целочисленное уменьшение; не намного дешевле, чем это ...
Этот ответ не выполняется, если строка содержит только пробелы.
Спасибо, @AbhinavGauniyal. Наконец-то я нашел время отредактировать ваш код в свой ответ.
Вместо "\ t" я бы сделал "\ t \ n"
Вышеупомянутые методы хороши, но иногда вы хотите использовать комбинацию функций для того, что ваша подпрограмма считает пробелом. В этом случае использование функторов для объединения операций может стать беспорядочным, поэтому я предпочитаю простой цикл, который я могу изменить для обрезки. Вот немного измененная функция обрезки, скопированная из версии C здесь, на SO. В этом примере я обрезаю не буквенно-цифровые символы.
string trim(char const *str)
{
// Trim leading non-letters
while(!isalnum(*str)) str++;
// Trim trailing non-letters
end = str + strlen(str) - 1;
while(end > str && !isalnum(*end)) end--;
return string(str, end+1);
}
забыл const char* end?
Вот что я придумал:
std::stringstream trimmer;
trimmer << str;
trimmer >> str;
Извлечение потока автоматически удаляет пробелы, поэтому это работает как шарм. К тому же довольно чистый и элегантный, если я так говорю. ;)
Хм; это предполагает, что в строке нет внутренних пробелов (например, пробелов). ОП только сказал, что хочет обрезать пробелы слева или справа.
Мне нравится решение tzaman, единственная проблема с ним в том, что он не обрезает строку, содержащую только пробелы.
Чтобы исправить этот 1 недостаток, добавьте str.clear () между двумя линиями триммера.
std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;
Хорошо :) Проблема с обоими нашими решениями заключается в том, что они обрезают оба конца; не могу сделать ltrim или rtrim такими.
Хорошо, но не может работать со строкой с внутренними пробелами. например trim (abc def ") -> abc, остается только abc.
Хорошее решение, если вы знаете, что внутренних пробелов не будет!
Это приятно и легко, но также довольно медленно, поскольку строка копируется в std::stringstream и из него.
В этой версии удаляются внутренние пробелы и символы, отличные от букв и цифр:
static inline std::string &trimAll(std::string &s)
{
if (s.size() == 0)
{
return s;
}
int val = 0;
for (int cur = 0; cur < s.size(); cur++)
{
if (s[cur] != ' ' && std::isalnum(s[cur]))
{
s[val] = s[cur];
val++;
}
}
s.resize(val);
return s;
}
Попробуй, у меня это работает.
inline std::string trim(std::string& str)
{
str.erase(0, str.find_first_not_of(' ')); //prefixing spaces
str.erase(str.find_last_not_of(' ')+1); //surfixing spaces
return str;
}
Если ваша строка не содержит пробелов в суффиксе, она будет удалена, начиная с npos + 1 == 0, и вы удалите всю строку.
@rgove Объясните, пожалуйста. str.find_last_not_of(x) возвращает позицию первого символа, не равную x. Он возвращает npos только в том случае, если ни один символ не соответствует x. В этом примере, если нет пробелов в суффиксах, он вернет эквивалент str.length() - 1, что даст по существу str.erase((str.length() - 1) + 1)., то есть, если я не сильно ошибаюсь.
Это не работает для строк, состоящих только из пробелов. (Добавление если не пустой перед вторым 'erase ()' исправляет это.) Также возвращает измененное значение, и изменение аргумента, переданного по ссылке, вероятно, вводит в заблуждение при чтении кода с использованием возвращаемого значения.
@JohannesOvermann: Почему он должен терпеть неудачу для строк, состоящих только из пробелов?
@robert: Ага, да, ладно. Это работает, потому что на практике npos + 1 == 0. Но я не очень люблю арифметику с npos. :-)
Это должно вернуть std :: string &, чтобы избежать ненужного вызова конструктора копирования.
Я не понимаю, почему это возвращает копию после изменения возвращаемого параметра?
@Galik Итак, вы можете обрезать и назначать или возвращать результат одновременно, например std::string myString(trim(myOtherString)) или return Trim(myString). Общая практика.
@MiloDC. Я не понимаю, зачем возвращать копию вместо по ссылке. Мне больше смысла возвращать std::string&.
Если вы измените порядок (сначала удалите пробелы в суффиксе, а затем в префиксе), он будет более эффективным.
Еще один вариант - убирает один или несколько символов с обоих концов.
string strip(const string& s, const string& chars = " ") {
size_t begin = 0;
size_t end = s.size()-1;
for(; begin < s.size(); begin++)
if (chars.find_first_of(s[begin]) == string::npos)
break;
for(; end > begin; end--)
if (chars.find_first_of(s[end]) == string::npos)
break;
return s.substr(begin, end-begin+1);
}
Мое решение на основе ответ @Bill the Lizard.
Обратите внимание, что эти функции возвращают пустую строку, если входная строка не содержит ничего, кроме пробелов.
const std::string StringUtils::WHITESPACE = " \n\r\t";
std::string StringUtils::Trim(const std::string& s)
{
return TrimRight(TrimLeft(s));
}
std::string StringUtils::TrimLeft(const std::string& s)
{
size_t startpos = s.find_first_not_of(StringUtils::WHITESPACE);
return (startpos == std::string::npos) ? "" : s.substr(startpos);
}
std::string StringUtils::TrimRight(const std::string& s)
{
size_t endpos = s.find_last_not_of(StringUtils::WHITESPACE);
return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
}
Немного поздно на вечеринку, но неважно. Теперь здесь C++ 11, у нас есть лямбда-выражения и автоматические переменные. Итак, моя версия, которая также обрабатывает все пробелы и пустые строки, такова:
#include <cctype>
#include <string>
#include <algorithm>
inline std::string trim(const std::string &s)
{
auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
auto wsback=std::find_if_not(s.rbegin(),s.rend(),[](int c){return std::isspace(c);}).base();
return (wsback<=wsfront ? std::string() : std::string(wsfront,wsback));
}
Мы могли бы сделать обратный итератор из wsfront и использовать его в качестве условия завершения во втором find_if_not, но это полезно только в случае строки, состоящей только из пробелов, а gcc 4.8 по крайней мере недостаточно умен, чтобы вывести тип обратного итератор (std::string::const_reverse_iterator) с auto. Я не знаю, насколько дорого обходится создание обратного итератора, поэтому здесь YMMV. С этим изменением код выглядит так:
inline std::string trim(const std::string &s)
{
auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
return std::string(wsfront,std::find_if_not(s.rbegin(),std::string::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base());
}
Хороший. +1 от меня. Жаль, что C++ 11 не ввел функцию trim () в std :: string и облегчил жизнь всем.
Я всегда хочу, чтобы один вызов функции обрезал строку, а не реализовывал ее
Как бы то ни было, эту лямбду использовать не нужно. Вы можете просто передать std::isspace: auto wsfront=std::find_if_not(s.begin(),s.end(),std::isspace);
+1, вероятно, единственный ответ с реализацией, которая копирует только одну строку O (N).
Компиляторы @vmrob не обязательно настолько умны. делать то, что вы говорите, двусмысленно: candidate template ignored: couldn't infer template argument '_Predicate' find_if_not(_InputIterator __first, _InputIterator __last, _Predicate __pred)
@johnbakers Он работает в Visual Studio (cl 19), но не в clang (3.9.1) или gcc (6.3).
Также я заметил, что количество сборочных линий для этого ответа (1-я функция) составляет ~ 1100 строк, в то время как, например, этот ответ составляет <100 строк. Однако дальше этого не исследовали.
Мне интересно, что не означает .base()? не могли бы вы сказать мне, пожалуйста
На самом деле это RTFM. std::reverse_iterator::base возвращает базовый итератор, на который указывает конкретный reverse_iterator. Обратные итераторы всегда указывают на один шаг за своим базовым итератором (то есть со смещением -1), поэтому base() используется для исправления ссылочного элемента. Фактически rev_iter.base() - 1 == rev_iter.
@vmrob Нет, нельзя. isspace имеет две перегрузки. Более того, начиная с C++ 20, взятие адреса функции в стандартной библиотеке - это UB.
@ L.F. Ой, давай. Я написал этот ответ 5 лет назад. Об этом лучше предупредить компилятор. Какая вторая перегрузка? Неофициальные документы оставляют меня в подвешенном состоянии: en.cppreference.com/w/cpp/string/byte/isspace
@vmrob другая перегрузка - это локаль. Однако ::isspace работал до C++ 20 (при условии, что вы включили заголовок C). На самом деле, дополнительная проблема заключается в том, что аргумент должен быть приведен к unsigned char перед передачей в isspace, но это уже другая история.
@ L.F. Ах я вижу! Спасибо за информацию!
В C++ 11 также появился модуль регулярное выражение, который, конечно, можно использовать для обрезки начальных или конечных пробелов.
Может быть, примерно так:
std::string ltrim(const std::string& s)
{
static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended};
return std::regex_replace(s, lws, "");
}
std::string rtrim(const std::string& s)
{
static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended};
return std::regex_replace(s, tws, "");
}
std::string trim(const std::string& s)
{
return ltrim(rtrim(s));
}
Как насчет этого...?
#include <iostream>
#include <string>
#include <regex>
std::string ltrim( std::string str ) {
return std::regex_replace( str, std::regex("^\\s+"), std::string("") );
}
std::string rtrim( std::string str ) {
return std::regex_replace( str, std::regex("\\s+$"), std::string("") );
}
std::string trim( std::string str ) {
return ltrim( rtrim( str ) );
}
int main() {
std::string str = " \t this is a test string \n ";
std::cout << "-" << trim( str ) << "-\n";
return 0;
}
Примечание: я все еще относительно новичок в C++, поэтому, пожалуйста, простите меня, если я здесь не на базе.
Использование regex для обрезки - это немного излишне.
Это намного более интенсивно, чем некоторые другие представленные варианты?
Это то, что я использую. Просто продолжайте убирать пространство спереди, а затем, если что-то осталось, сделайте то же самое сзади.
void trim(string& s) {
while(s.compare(0,1," ")==0)
s.erase(s.begin()); // remove leading whitespaces
while(s.size()>0 && s.compare(s.size()-1,1," ")==0)
s.erase(s.end()-1); // remove trailing whitespaces
}
Думаю, если вы начнете спрашивать «лучший способ» обрезать строку, я бы сказал, что хорошей реализацией будет такая, которая:
Очевидно, что существует слишком много разных подходов к этому, и это определенно зависит от того, что вам действительно нужно. Однако в стандартной библиотеке C все еще есть некоторые очень полезные функции в <string.h>, например memchr. Есть причина, по которой C по-прежнему считается лучшим языком для ввода-вывода - его stdlib - это чистая эффективность.
inline const char* trim_start(const char* str)
{
while (memchr(" \t\n\r", *str, 4)) ++str;
return str;
}
inline const char* trim_end(const char* end)
{
while (memchr(" \t\n\r", end[-1], 4)) --end;
return end;
}
inline std::string trim(const char* buffer, int len) // trim a buffer (input?)
{
return std::string(trim_start(buffer), trim_end(buffer + len));
}
inline void trim_inplace(std::string& str)
{
str.assign(trim_start(str.c_str()),
trim_end(str.c_str() + str.length()));
}
int main()
{
char str [] = "\t \nhello\r \t \n";
string trimmed = trim(str, strlen(str));
cout << "'" << trimmed << "'" << endl;
system("pause");
return 0;
}
std::string trim( std::string && str )
{
size_t end = str.find_last_not_of( " \n\r\t" );
if ( end != std::string::npos )
str.resize( end + 1 );
size_t start = str.find_first_not_of( " \n\r\t" );
if ( start != std::string::npos )
str = str.substr( start );
return std::move( str );
}
std::string trim(const std::string &s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && isspace(*it))
it++;
std::string::const_reverse_iterator rit = s.rbegin();
while (rit.base() != it && isspace(*rit))
rit++;
return std::string(it, rit.base());
}
Наконец-то изящное решение для базовой отделки пространства ... :)
Как это работает: это решение, подобное копированию - оно находит позицию первого символа, не являющуюся пробелом (it), и обратное: положение символа, после которого остаются только пробелы (rit) - после этого он возвращает вновь созданную строку == копия части исходной строки - часть, основанная на этих итераторах ...
Спасибо, у меня сработало: std: string s = "Oh noez: space \ r \ n"; std :: string clean = обрезка (и);
Это хорошо? (Потому что этот пост полностью нуждается в другом ответе :)
string trimBegin(string str)
{
string whites = "\t\r\n ";
int i = 0;
while (whites.find(str[i++]) != whites::npos);
str.erase(0, i);
return str;
}
Аналогичный случай для trimEnd, просто поменяйте полярность, индексы.
Я не совсем уверен, каковы накладные расходы при использовании временной строки для сравнения, но я думаю, что для ясности я бы предпочел использовать while (isspace(str[i++]));. Самым большим мотивирующим фактором на самом деле является то, что мне пришлось прочитать некоторую документацию, чтобы понять код, потому что я изначально думал, что это имеет порядок сложности O (n ^ 2) (чего, как вы знаете, нет). Затем код можно было бы сократить до string trimBegin(string str) { size_t i = 0; while(isspace(str[i++]); return str.erase(0, i); }.
Свой вклад в устранение шума. trim по умолчанию создает новую строку и возвращает измененную, в то время как trim_in_place изменяет переданную ей строку. Функция trim поддерживает семантику перемещения C++ 11.
#include <string>
// modifies input string, returns input
std::string& trim_left_in_place(std::string& str) {
size_t i = 0;
while(i < str.size() && isspace(str[i])) { ++i; };
return str.erase(0, i);
}
std::string& trim_right_in_place(std::string& str) {
size_t i = str.size();
while(i > 0 && isspace(str[i - 1])) { --i; };
return str.erase(i, str.size());
}
std::string& trim_in_place(std::string& str) {
return trim_left_in_place(trim_right_in_place(str));
}
// returns newly created strings
std::string trim_right(std::string str) {
return trim_right_in_place(str);
}
std::string trim_left(std::string str) {
return trim_left_in_place(str);
}
std::string trim(std::string str) {
return trim_left_in_place(trim_right_in_place(str));
}
#include <cassert>
int main() {
std::string s1(" \t\r\n ");
std::string s2(" \r\nc");
std::string s3("c \t");
std::string s4(" \rc ");
assert(trim(s1) == "");
assert(trim(s2) == "c");
assert(trim(s3) == "c");
assert(trim(s4) == "c");
assert(s1 == " \t\r\n ");
assert(s2 == " \r\nc");
assert(s3 == "c \t");
assert(s4 == " \rc ");
assert(trim_in_place(s1) == "");
assert(trim_in_place(s2) == "c");
assert(trim_in_place(s3) == "c");
assert(trim_in_place(s4) == "c");
assert(s1 == "");
assert(s2 == "c");
assert(s3 == "c");
assert(s4 == "c");
}
То, что вы делаете, хорошо и надежно. Я использовал один и тот же метод в течение долгого времени, и мне еще предстоит найти более быстрый метод:
const char* ws = " \t\n\r\f\v";
// trim from end of string (right)
inline std::string& rtrim(std::string& s, const char* t = ws)
{
s.erase(s.find_last_not_of(t) + 1);
return s;
}
// trim from beginning of string (left)
inline std::string& ltrim(std::string& s, const char* t = ws)
{
s.erase(0, s.find_first_not_of(t));
return s;
}
// trim from both ends of string (right then left)
inline std::string& trim(std::string& s, const char* t = ws)
{
return ltrim(rtrim(s, t), t);
}
Предоставляя символы для обрезки, вы получаете возможность обрезать непробельные символы и эффективно обрезать только те символы, которые вы хотите обрезать.
если вы измените порядок в trim, т.е. сделаете его rtrim(ltrim(s, t), t), это будет немного эффективнее
@CITBL Сначала выполняется внутренняя функция, поэтому она будет обрезать слева до обрезку справа. Я думаю, это было бы меньше эффективным, не так ли?
Точно. Виноват
если вы используете basic_string и template в CharT, вы можете сделать это для всех строк, просто используйте переменную шаблона для пробела, чтобы вы использовали ее как ws <CharT>. технически на этом этапе вы могли бы подготовить его для C++ 20 и также пометить его constexpr, поскольку это подразумевает встроенный
@ Действительно на пляже. Хотя здесь немного сложно дать ответ. Я написал для этого шаблонные функции, и это, безусловно, довольно сложно. Я пробовал кучу разных подходов и до сих пор не уверен, какой из них лучший.
Как бы то ни было, вот урезанная реализация с прицелом на производительность. Это намного быстрее, чем многие другие процедуры обрезки, которые я видел. Вместо использования итераторов и std :: find он использует необработанные строки и индексы c. Он оптимизирует следующие особые случаи: строка размером 0 (ничего не делать), строка без пробелов для обрезки (ничего не делать), строка только с конечными пробелами для обрезки (просто измените размер строки), строка, полностью состоящая из пробелов (просто очистите строку) . И, наконец, в худшем случае (строка с ведущими пробелами) он делает все возможное, чтобы выполнить эффективное построение копии, выполняя только одну копию, а затем перемещая эту копию вместо исходной строки.
void TrimString(std::string & str)
{
if (str.empty())
return;
const auto pStr = str.c_str();
size_t front = 0;
while(front < str.length() && std::isspace(int(pStr[front]))) {++front;}
size_t back = str.length();
while(back > front && std::isspace(int(pStr[back-1]))) {--back;}
if (0 == front)
{
if (back < str.length())
{
str.resize(back - front);
}
}
else if (back <= front)
{
str.clear();
}
else
{
str = std::move(std::string(str.begin()+front, str.begin()+back));
}
}
@bmgda, возможно, теоретически самая быстрая версия могла бы иметь такую сигнатуру: extern "C" void string_trim (char ** begin_, char ** end_) ... Поймайте мой дрейф?
Мой ответ - улучшение главный ответ для этого сообщения, которое обрезает управляющие символы, а также пробелы (0-32 и 127 на Таблица ASCII).
std::isgraph определяет, имеет ли символ графическое представление, поэтому вы можете использовать это, чтобы изменить ответ Эвана, чтобы удалить любой символ, который не имеет графического представления с обеих сторон строки. В результате получилось гораздо более элегантное решение:
#include <algorithm>
#include <functional>
#include <string>
/**
* @brief Left Trim
*
* Trims whitespace from the left end of the provided std::string
*
* @param[out] s The std::string to trim
*
* @return The modified std::string&
*/
std::string& ltrim(std::string& s) {
s.erase(s.begin(), std::find_if (s.begin(), s.end(),
std::ptr_fun<int, int>(std::isgraph)));
return s;
}
/**
* @brief Right Trim
*
* Trims whitespace from the right end of the provided std::string
*
* @param[out] s The std::string to trim
*
* @return The modified std::string&
*/
std::string& rtrim(std::string& s) {
s.erase(std::find_if (s.rbegin(), s.rend(),
std::ptr_fun<int, int>(std::isgraph)).base(), s.end());
return s;
}
/**
* @brief Trim
*
* Trims whitespace from both ends of the provided std::string
*
* @param[out] s The std::string to trim
*
* @return The modified std::string&
*/
std::string& trim(std::string& s) {
return ltrim(rtrim(s));
}
Примечание: В качестве альтернативы вы можете использовать std::iswgraph, если вам нужна поддержка широких символов, но вам также придется отредактировать этот код, чтобы включить манипуляции с std::wstring, что я не тестировал (см. Справочную страницу для std::basic_string, чтобы изучить это вариант).
std :: ptr_fun устарел
Элегантный способ сделать это может выглядеть как
std::string & trim(std::string & str)
{
return ltrim(rtrim(str));
}
А вспомогательные функции реализованы как:
std::string & ltrim(std::string & str)
{
auto it = std::find_if ( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
str.erase( str.begin() , it);
return str;
}
std::string & rtrim(std::string & str)
{
auto it = std::find_if ( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
str.erase( it.base() , str.end() );
return str;
}
И как только вы все это разместите, вы также можете написать это:
std::string trim_copy(std::string const & str)
{
auto s = str;
return ltrim(rtrim(s));
}
Я использую вот это:
void trim(string &str){
int i=0;
//left trim
while (isspace(str[i])!=0)
i++;
str = str.substr(i,str.length()-i);
//right trim
i=str.length()-1;
while (isspace(str[i])!=0)
i--;
str = str.substr(0,i+1);
}
s.erase(0, s.find_first_not_of(" \n\r\t"));
s.erase(s.find_last_not_of(" \n\r\t")+1);
Было бы немного эффективнее, если вы сделаете это в обратном порядке и сначала обрежете справа, а затем вызовете сдвиг, обрезав левый.
Обрезать реализацию C++ 11:
static void trim(std::string &s) {
s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), [](char c){ return std::isspace(c); }));
s.erase(std::find_if_not(s.rbegin(), s.rend(), [](char c){ return std::isspace(c); }).base(), s.end());
}
Похоже, я очень опаздываю на вечеринку - не могу поверить, что об этом спросили 7 лет назад!
Вот мой взгляд на проблему. Я работаю над проектом, и я не хотел сейчас испытывать трудности с использованием Boost.
std::string trim(std::string str) {
if (str.length() == 0) return str;
int beg = 0, end = str.length() - 1;
while (str[beg] == ' ') {
beg++;
}
while (str[end] == ' ') {
end--;
}
return str.substr(beg, end - beg + 1);
}
Это решение будет обрезать слева и справа.
с ++ 11:
int i{};
string s = " h e ll \t\n o";
string trim = " \n\t";
while ((i = s.find_first_of(trim)) != -1)
s.erase(i,1);
cout << s;
выход:
hello
отлично работает также с пустыми строками
Ваш код удаляет все пробелы даже между непробелами. Это не то, что должна делать "обрезка".
Поскольку я хотел обновить свою старую функцию обрезки C++ с помощью подхода C++ 11, я протестировал множество опубликованных ответов на вопрос. Я пришел к выводу, что я сохраняю свое старое решение на C++!
По большому счету, это самый быстрый, даже добавление большего количества символов для проверки (например, \ r \ n, я не вижу варианта использования для \ f \ v) все равно быстрее, чем решения, использующие алгоритм.
std::string & trimMe (std::string & str)
{
// right trim
while (str.length () > 0 && (str [str.length ()-1] == ' ' || str [str.length ()-1] == '\t'))
str.erase (str.length ()-1, 1);
// left trim
while (str.length () > 0 && (str [0] == ' ' || str [0] == '\t'))
str.erase (0, 1);
return str;
}
Вот простая реализация. Для такой простой операции вам, вероятно, не следует использовать какие-либо специальные конструкции. Встроенная функция isspace () заботится о различных формах белых символов, поэтому мы должны воспользоваться ею. Вы также должны учитывать особые случаи, когда строка пуста или просто набор пробелов. Обрезка влево или вправо может быть получена из следующего кода.
string trimSpace(const string &str) {
if (str.empty()) return str;
string::size_type i,j;
i=0;
while (i<str.size() && isspace(str[i])) ++i;
if (i == str.size())
return string(); // empty string
j = str.size() - 1;
//while (j>0 && isspace(str[j])) --j; // the j>0 check is not needed
while (isspace(str[j])) --j
return str.substr(i, j-i+1);
}
Вот моя версия:
size_t beg = s.find_first_not_of(" \r\n");
return (beg == string::npos) ? "" : in.substr(beg, s.find_last_not_of(" \r\n") - beg);
Вам не хватает последнего символа. +1 в длине решает эту проблему.
Вот решение, легкое для понимания новичками, которые не привыкли везде писать std:: и еще не знакомы с корректностью const, iterator, STL algorithm и т. д.
#include <string>
#include <cctype> // for isspace
using namespace std;
// Left trim the given string (" hello! " --> "hello! ")
string left_trim(string str) {
int numStartSpaces = 0;
for (int i = 0; i < str.length(); i++) {
if (!isspace(str[i])) break;
numStartSpaces++;
}
return str.substr(numStartSpaces);
}
// Right trim the given string (" hello! " --> " hello!")
string right_trim(string str) {
int numEndSpaces = 0;
for (int i = str.length() - 1; i >= 0; i--) {
if (!isspace(str[i])) break;
numEndSpaces++;
}
return str.substr(0, str.length() - numEndSpaces);
}
// Left and right trim the given string (" hello! " --> "hello!")
string trim(string str) {
return right_trim(left_trim(str));
}
Надеюсь, это поможет...
Это так раздражает, что я
Для меня это самый быстрый способ решить:
CString tmp(line.c_str());
tmp = tmp.Trim().MakeLower();
string buffer = tmp;
Хорошо, это здорово, что я могу использовать лямбда-операторы, итераторы и все такое. Но мне нужно иметь дело только со строкой вместо символа ...
CString - это специфическая для Windows мерзость MFC / ATL (которая, возможно, ближе к «ракетостроению», чем несколько строк стандартного C++ без сторонних зависимостей). Вы не можете предположить, что кто-то может его использовать или даже хочет его использовать.Конечно, я не хочу его использовать, но это более короткий способ избежать записи большого количества итераторов. Я не горжусь этими строками кода, но должен быть более простой способ сделать это в stdlib
Я согласен с тем, что std :: string не хватает некоторых очевидных функций. Простые вещи не должны требовать нескольких строк кода.
Я знаю, что это очень старый вопрос, но я добавил к вам несколько строк кода, и он обрезает пробелы с обоих концов.
void trim(std::string &line){
auto val = line.find_last_not_of(" \n\r\t") + 1;
if (val == line.size() || val == std::string::npos){
val = line.find_first_not_of(" \n\r\t");
line = line.substr(val);
}
else
line.erase(val);
}
Ниже приведено однопроходное (может быть двухпроходное) решение. Он проходит через часть строки с пробелами дважды и часть без пробелов один раз.
void trim(std::string& s) {
if (s.empty())
return;
int l = 0, r = s.size() - 1;
while (l < s.size() && std::isspace(s[l++])); // l points to first non-whitespace char.
while (r >= 0 && std::isspace(s[r--])); // r points to last non-whitespace char.
if (l > r)
s = "";
else {
l--;
r++;
int wi = 0;
while (l <= r)
s[wi++] = s[l++];
s.erase(wi);
}
return;
}
В C++ 17 вы можете использовать basic_string_view :: remove_prefix и basic_string_view :: remove_suffix:
std::string_view trim(std::string_view s)
{
s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
s.remove_suffix(std::min(s.size() - s.find_last_not_of(" \t\r\v\n") - 1, s.size()));
return s;
}
Хорошая альтернатива:
std::string_view ltrim(std::string_view s)
{
s.remove_prefix(std::distance(s.cbegin(), std::find_if (s.cbegin(), s.cend(),
[](int c) {return !std::isspace(c);})));
return s;
}
std::string_view rtrim(std::string_view s)
{
s.remove_suffix(std::distance(s.crbegin(), std::find_if (s.crbegin(), s.crend(),
[](int c) {return !std::isspace(c);})));
return s;
}
std::string_view trim(std::string_view s)
{
return ltrim(rtrim(s));
}
Я не уверен, что вы тестируете, но в вашем примере std :: find_first_not_of вернет std :: string :: npos, а std :: string_view :: size вернет 4. Очевидно, что минимум четыре, количество элементов, которые должны быть удалены std :: string_view :: remove_prefix. И gcc 9.2, и clang 9.0 справляются с этим правильно: godbolt.org/z/DcZbFH
Хорошо, это может быть не самый быстрый, но ... простой.
str = " aaa ";
int len = str.length();
// rtrim
while(str[len-1] == ' ') { str.erase(--len,1); }
// ltrim
while(str[0] == ' ') { str.erase(0,1); }
Это неверно. Вам нужно инвертировать ltrim и rtrim, чтобы это работало.
Это неверно: это вызывает ошибку исключения массива, если str= = "" или str= = " " (три пробела). Чтобы исправить это, добавьте проверку !copy.empty() && в качестве проверки первый для обоих циклов while. Реализация rtrim сдвигает всю строку вниз на каждом шаге, что может быть неэффективным. Если важна эффективность, предложите другие ответы, в которых выполняется сканирование с последующей единственной операцией обрезки.
Вот решение для обрезки с регулярным выражением
#include <string>
#include <regex>
string trim(string str){
return regex_replace(str, regex("(^[ ]+)|([ ]+$)"),"");
}
Принятый ответ и даже версия Boost у меня не работали, поэтому я написал следующую версию:
std::string trim(const std::string& input) {
std::stringstream string_stream;
for (const auto character : input) {
if (!isspace(character)) {
string_stream << character;
}
}
return string_stream.str();
}
Это удалит любой пробельный символ из любого места в строке и вернет новую копию строки.
Я прочитал большинство ответов, но не нашел никого, кто использовал бы istringstream
.
std::string text = "Let me split this into words";
std::istringstream iss(text);
std::vector<std::string> results((std::istream_iterator<std::string>(iss)),
std::istream_iterator<std::string>());
Результатом является вектор слов, и он может работать со строками, имеющими внутренние пробелы, Надеюсь, это помогло.
почему бы не использовать лямбду?
auto no_space = [](char ch) -> bool {
return !std::isspace<char>(ch, std::locale::classic());
};
auto ltrim = [](std::string& s) -> std::string& {
s.erase(s.begin(), std::find_if (s.begin(), s.end(), no_space));
return s;
};
auto rtrim = [](std::string& s) -> std::string& {
s.erase(std::find_if (s.rbegin(), s.rend(), no_space).base(), s.end());
return s;
};
auto trim_copy = [](std::string s) -> std::string& { return ltrim(rtrim(s)); };
auto trim = [](std::string& s) -> std::string& { return ltrim(rtrim(s)); };
Обрезает оба конца.
string trim(const std::string &str){
string result = "";
size_t endIndex = str.size();
while (endIndex > 0 && isblank(str[endIndex-1]))
endIndex -= 1;
for (size_t i=0; i<endIndex ; i+=1){
char ch = str[i];
if (!isblank(ch) || result.size()>0)
result += ch;
}
return result;
}
str.erase(0, str.find_first_not_of("\t\n\v\f\r ")); // left trim
str.erase(str.find_last_not_of("\t\n\v\f\r ") + 1); // right trim
Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением. - Из обзора
Обрезка шнурка бедняги (только пробелы):
std::string trimSpaces(const std::string& str)
{
int start, len;
for (start = 0; start < str.size() && str[start] == ' '; start++);
for (len = str.size() - start; len > 0 && str[start + len - 1] == ' '; len--);
return str.substr(start, len);
}
Ответы на этот вопрос свидетельствуют о том, насколько не хватает стандартной библиотеки C++.