Каков предпочтительный способ удаления пробелов из строки в C++? Я мог бы перебрать все символы и построить новую строку, но есть ли способ лучше?





string.erase(std::remove_if (string.begin(), string.end(), std::isspace), string.end());
Также обратите внимание, что если какой-либо из символов отрицательный (например, символ UTF8, когда char подписан), использование ::isspace - это UB.
std::string::iterator end_pos = std::remove(str.begin(), str.end(), ' ');
str.erase(end_pos, str.end());
Мой голос за каноническую идиому удаления / удаления. Может быть преобразован в один лайнер: str.erase (std :: remove (str.begin (), str.end (), ''), str.end ());
Примечание: вам необходимо включить <algorithm>, чтобы это работало.
Боюсь, это лучшее решение, которое я могу придумать. Но вы можете использовать Reserve (), чтобы заранее выделить минимально необходимую память, чтобы немного ускорить работу. В итоге вы получите новую строку, которая, вероятно, будет короче, но займет такой же объем памяти, но вы избежите перераспределения.
Обновлено: в зависимости от вашей ситуации это может повлечь меньше накладных расходов, чем беспорядочные символы.
Вам следует попробовать разные подходы и посмотреть, что лучше для вас: у вас может вообще не быть проблем с производительностью.
remove_if создает не более одной копии каждого значения. Так что накладных расходов на то, что нужно сделать, действительно не так уж и много.
Лучше всего использовать алгоритм remove_if и isspace:
remove_if (str.begin(), str.end(), isspace);
Теперь сам алгоритм не может изменить контейнер (только изменить значения), поэтому он фактически перемешивает значения и возвращает указатель на то место, где теперь должен быть конец. Итак, мы должны вызвать string :: erase, чтобы фактически изменить длину контейнера:
str.erase(remove_if (str.begin(), str.end(), isspace), str.end());
Также следует отметить, что remove_if сделает не более одной копии данных. Вот пример реализации:
template<typename T, typename P>
T remove_if (T beg, T end, P pred)
{
T dest = beg;
for (T itr = beg;itr != end; ++itr)
if (!pred(*itr))
*(dest++) = *itr;
return dest;
}
Поскольку у "isspace" есть перегрузки, вам, вероятно, потребуется квалифицировать общий код для использования :: isspace (реализация C, которая не принимает языковой стандарт) или столкнуться с загадочными ошибками создания экземпляров шаблона.
Все - будьте осторожны с вышеуказанным методом (две отдельные строки, а не шаблонная версия, хотя у нее может быть та же проблема). Я использовал его в проекте, не понимая, что это не всегда правильно. Например, если вы передадите ему строку «1 + 1», он вернет «1 + 11». Я переключился на метод @rupello, описанный ниже, и в этом случае он отлично сработал. Удачного кодирования!
@Joe В ответе прямо упоминается, что вам нужно вызвать erase после этого. Это вернет правильный результат.
-1 это использование isspace - это UB для всех наборов символов, кроме исходного 7-битного ASCII. C99 §7.4 / 1. Это не удивляет мне, что к настоящему моменту за него проголосовали 71 голос, несмотря на то, что он является очень плохим советом.
Я не знаю почему, но remove_if работает неправильно в OS X
@ Cheersandhth.-Alf: c99 isspace() принимает любое значение int, представленное как unsigned char (+ EOF). Не вижу UB для std::string. Что мне здесь не хватает?
@ J.F.Sebastian: вам просто не хватает того, что для большинства (если не только «всех») существующих компиляторов C++ char по умолчанию является типом целое число со знаком. Таким образом, для символов, отличных от ASCII, вы получаете отрицательные значения. Например, с Latin-1 и выбором подписи по умолчанию норвежские символы ÆØÅ и æøå являются отрицательными.
@ Cheersandhth.-Альф: Информация не потеряна: char -> int. Все signed char "могут быть представлены" как unsigned char. ::isspace() возвращает тот же результат с gcc, clang -std = C++ 11. Вы хотите сказать, что «почти все» компиляторы C++ не включают gcc, clang? Или они не следуют стандарту какой-то другой (не моей) системы?
@ J.F.Sebastian: когда вы передаете отрицательное значение, кроме EOF, в isspace, вы получаете Undefined Behavior. потому что стандарт C++ говорит об этом путем включения стандарта C. это так просто. вы можете связать это со своим аргументом (который имеет другой вывод), чтобы выявить изъян или изъяны. в любом случае код в этом ответе передает отрицательные значения для всех символов, отличных от ASCII, с подписью по умолчанию char. Таким образом, у него есть Неопределенное поведение, который может имеет эффект, которого вы наивно и неправильно ожидаете. Это базовые знания.
Чтобы попробовать это, вы можете поэкспериментировать с различными функциями классификации символов в отладочных сборках с помощью Visual C++. Некоторые из реализаций управляются таблицами и будут давать сбой при использовании отрицательных аргументов, отличных от EOF. Вот что означает наличие UB: в некоторых случаях с некоторыми компиляторами или контекстом вы получаете другое поведение, отличное от того, что вы наивно и неправильно ожидаете, то есть непереносимый код.
@ Cheersandhth.-Альф: "потому что стандарт C++ так говорит" - где это написано? Мы уже установили, что C99 §7.4/1 не говорит этого, если вы не утверждаете, что char не может быть представлен как unsigned char. Я проверил, что и gcc, и clang дают ожидаемый результат (isspace() имеет одинаковое значение для char и unsigned char) на моей машине. Не работает ли код в Visual C++? Кроме того, Microsoft является «стандартом», но не стандартом C++ 11.
@ J.F.Sebastian: около половины значений signed char не могут быть представлены как unsigned char в значении представимых значений, используемом в стандарте. та же терминология используется для преобразования из беззнакового в подписанный, где «в противном случае значение определяется реализацией» (C++ 11 §4.7 / 3). таким образом, вы фактически утверждаете, что слово «представимый», используемое в отношении одного и того же, означает две совершенно разные вещи в двух разных частях стандарта. если так, то это неизвестный до сих пор дефект в стандарте. вы должны сообщить об этом.
Чтобы повторить, код в этом ответе передает отрицательные значения (отличные от EOF) в isspace для всех символов, отличных от ASCII, с практическим выбором подписи по умолчанию для char. Таким образом, у него есть неопределенное поведение. Я повторяю это, потому что подозреваю, что это умышленная попытка заглушить этот факт.
@ Cheersandhth.-Альф: ты прав. +1. Это неопределенное поведение, чтобы передать отрицательный int, который не является EOF, в isspace() в C11. Мне потребовалось задать вопрос что означает «представимый» в C11?, чтобы понять. «Никогда не приписывайте злому умыслу то, что адекватно объясняется глупостью» :)
Мало того, я думаю, что MSVC на самом деле утверждает, что это означает, что ваша программа буду выйдет из строя, если вы сделаете это для некоторого ввода.
error: no matching function for call to ‘remove_if (std::__cxx11::basic_string<char>::iterator, std::__cxx11::basic_string<char>::iterator, <unresolved overloaded function type>)’
No matching function for call to 'remove_if' даже включал библиотеку алгоритмов.
Будет ли это быстрее, чем regex_replace (inputString, "\\ s +", "")?
Представьте, поддерживает ли C++ str.replace ("", ""); Как бы жизнь была намного проще!
Можете ли вы использовать алгоритм Boost String? http://www.boost.org/doc/libs/1_35_0/doc/html/string_algo/usage.html#id1290573
erase_all(str, " ");
Он медленнее, чем remove_if (str.begin(), str.end(), isspace);, о котором упоминал Мэтт Прайс. Не знаю почему. На самом деле, все средства повышения, у которых есть альтернативы STL, медленнее, чем соответствующие gcc (все те, которые я тестировал). Некоторые из них намного медленнее! (до 5 раз во вставках unordered_map) Возможно, это из-за кеш-памяти ЦП общей среды или чего-то в этом роде.
Для обрезки используйте алгоритмы ускорения строки:
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace boost;
// ...
string str1(" hello world! ");
trim(str1); // str1 == "hello world!"
Привет, ты можешь сделать что-то подобное. Эта функция удаляет все пробелы.
string delSpaces(string &str)
{
str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
return str;
}
Я сделал еще одну функцию, которая удаляет все ненужные пробелы.
string delUnnecessary(string &str)
{
int size = str.length();
for(int j = 0; j<=size; j++)
{
for(int i = 0; i <=j; i++)
{
if (str[i] == ' ' && str[i+1] == ' ')
{
str.erase(str.begin() + i);
}
else if (str[0]== ' ')
{
str.erase(str.begin());
}
else if (str[i] == '\0' && str[i-1]== ' ')
{
str.erase(str.end() - 1);
}
}
}
return str;
}
string replaceinString(std::string str, std::string tofind, std::string toreplace)
{
size_t position = 0;
for ( position = str.find(tofind); position != std::string::npos; position = str.find(tofind,position) )
{
str.replace(position ,1, toreplace);
}
return(str);
}
используй это:
string replace = replaceinString(thisstring, " ", "%20");
string replace2 = replaceinString(thisstring, " ", "-");
string replace3 = replaceinString(thisstring, " ", "+");
Вы можете использовать это решение для удаления символа:
#include <algorithm>
#include <string>
using namespace std;
str.erase(remove(str.begin(), str.end(), char_to_remove), str.end());
#include <string.h> с использованием пространства имен std;
Это решение мне подходит. Верхний - нет.
следует избегать использования пространства имен std. stackoverflow.com/questions/1452721/…
string removespace(string str)
{
int m = str.length();
int i=0;
while(i<m)
{
while(str[i] == 32)
str.erase(i,1);
i++;
}
}
Обычно рекомендуется добавлять краткое пояснение к ответам кода.
@test - length() возвращает size_t, а не int. erase() принимает size_type, а не int. Функция, вероятно, завершится ошибкой, если встретятся два последовательных пробела, поскольку индекс всегда увеличивается. Если один пробел удален, цикл будет читать за пределами строки. Вам, вероятно, следует удалить этот ответ, поскольку он требует большой помощи.
Если вы хотите сделать это с помощью простого макроса, вот один:
#define REMOVE_SPACES(x) x.erase(std::remove(x.begin(), x.end(), ' '), x.end())
Это, конечно, предполагает, что вы уже использовали #include <string>.
Назовите это так:
std::string sName = " Example Name ";
REMOVE_SPACES(sName);
printf("%s",sName.c_str()); // requires #include <stdio.h>
зачем вам использовать для этого макрос?
Меньше набора текста с клавиатуры для выполнения обычных задач.
Столь же коротко для call-site вызывает функция, принимающий lvalue-ссылку на строку. Макросы могут иметь неожиданное поведение при взаимодействии со своими аргументами (особенно с побочными эффектами), но, что еще хуже, если они вовлечены в ошибку, их имена не отображаются в сообщениях компилятора, в отличие от их реализации.
Да - макросы могут очень затруднить отладку и обслуживание. В небольшой программе, может быть, они в порядке. В многомиллионном приложении с сотнями проектов макросы действительно могут стать проблемой.
Я долго использовал описанную ниже работу - не уверен в ее сложности.
s.erase(std::unique(s.begin(),s.end(),[](char s,char f){return (f==' '||s==' ');}),s.end());
когда вы хотите удалить символ ' ', а некоторые, например, -, используют
s.erase(std::unique(s.begin(),s.end(),[](char s,char f){return ((f==' '||s==' ')||(f=='-'||s=='-'));}),s.end());
аналогично просто увеличьте ||, если количество символов, которые вы хотите удалить, не равно 1
но, как упоминалось другими, идиома стирания удаления также кажется прекрасной.
string removeSpaces(string word) {
string newWord;
for (int i = 0; i < word.length(); i++) {
if (word[i] != ' ') {
newWord += word[i];
}
}
return newWord;
}
Этот код в основном берет строку и перебирает все символы в ней. Затем он проверяет, является ли эта строка пробелом, если это не так, то символ добавляется в новую строку.
#include <algorithm> using namespace std; int main() { . . s.erase( remove( s.begin(), s.end(), ' ' ), s.end() ); . . }
Ссылка взята с форума это.
На самом деле это не добавляет ничего, кроме того, что уже делает этот ответ. Есть ли дополнительные объяснения или подробности, которые вы могли бы добавить, чтобы сделать ваш ответ более качественным и заслуживающим внимания на этот вопрос?
Я думаю, что это больше проще, потому что он делает то же самое в одном операторе.
Большой! Затем изложите это рассуждение как объяснение прямо в вашем ответе. Исходный вопрос больше, чем Одиннадцать лет, и без обоснования ваш ответ может быть воспринят как шум по сравнению с другими принятыми ответами, получившими хорошие голоса. Наличие такого объяснения поможет предотвратить удаление вашего ответа.
Это будет хороший, но я не мог этого понять, как мне вставить который в свой ответ ... что мой ответ лучше, чем этот ответ.? Было бы очень приятно, если бы вы могли редактировать мой ответ.
К сожалению, редактирование вашего ответа, чтобы добавить этот контент сам, будет противоречить правила редактирования, и мое редактирование, вероятно, будет отклонено или откатано позже. Вы можете использовать первую ссылку в этом комментарии, чтобы отредактировать ответ самостоятельно. Совершенно приемлемо заявить, что, по вашему мнению, ваш ответ лучше, чем какой-либо другой, и дать ему обоснование. Сообщество решит, правы вы, голосуя за или против.
Пожалуйста, объясните, как в приведенной выше строке удаляются пробелы. Каков процесс выполнения. ??
В C++ 20 вы можете использовать бесплатную функцию std :: erase
std::string str = " Hello World !";
std::erase(str, ' ');
Полный пример:
#include<string>
#include<iostream>
int main() {
std::string str = " Hello World !";
std::erase(str, ' ');
std::cout << "|" << str <<"|";
}
Печатаю | так что очевидно, что пространство в начале также удаляется.
примечание: при этом удаляется только пробел, а не все другие возможные символы, которые могут считаться пробелами, см. https://en.cppreference.com/w/cpp/string/byte/isspace
Удаляет все пробельные символы, такие как табуляции и разрывы строк (C++ 11):
string str = " \n AB cd \t efg\v\n";
str = regex_replace(str,regex("\s"),"");
Почему вы рекомендуете этот подход вместо принятого ответа @ Matt-Price более десяти лет назад?
Пусть здесь будут представлены все решения. Может кому понадобится это решение.
Я не возражаю против этого. Я говорю, чтобы людям было легче оценивать разные подходы, объясняя различия и сценарии, для которых они могут лучше подходить.
Возможно это решение не самое экономичное, но оно позволяет избавиться от всех пробельные символы '\ s', а не только от пробелов ''.
string str = "2C F4 32 3C B9 DE";
str.erase(remove(str.begin(),str.end(),' '),str.end());
cout << str << endl;
вывод: 2CF4323CB9DE
Просто для удовольствия, так как другие ответы намного лучше, чем этот.
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/filter.hpp>
int main() {
using ranges::to;
using ranges::views::filter;
using boost::hana::partial;
auto const& not_space = partial(std::not_equal_to<>{}, ' ');
auto const& to_string = to<std::string>;
std::string input = "2C F4 32 3C B9 DE";
std::string output = input | filter(not_space) | to_string;
assert(output == "2CF4323CB9DE");
}
Я создал функцию, которая удаляет пробелы с обоих концов строки. Такие как
" Hello World " будет преобразован в "Hello world".
Это работает аналогично функциям strip, lstrip и rstrip, которые часто используются в Python.
string strip(string str) {
while (str[str.length() - 1] == ' ') {
str = str.substr(0, str.length() - 1);
}
while (str[0] == ' ') {
str = str.substr(1, str.length() - 1);
}
return str;
}
string lstrip(string str) {
while (str[0] == ' ') {
str = str.substr(1, str.length() - 1);
}
return str;
}
string rstrip(string str) {
while (str[str.length() - 1] == ' ') {
str = str.substr(0, str.length() - 1);
}
return str;
}
Это не будет компилироваться в реализациях, соответствующих стандартам, из-за перегрузок std :: isspace, связанных с локализацией. Вам нужно будет использовать :: isspace или выполнить некоторые нечитаемые махинации с помощью std :: bind2nd. Разве общий код не прекрасен?