Удаление начальных и конечных пробелов из строки

92

Как удалить пробелы из строкового объекта в C ++.
Например, как удалить начальные и конечные пробелы из объекта строки ниже.

//Original string: "         This is a sample string                    "
//Desired string: "This is a sample string"

Насколько мне известно, строковый класс не предоставляет никаких методов для удаления начальных и конечных пробелов.

Чтобы усугубить проблему, как расширить это форматирование для обработки лишних пробелов между словами строки. Например,

// Original string: "          This       is         a sample   string    " 
// Desired string:  "This is a sample string"  

Используя строковые методы, упомянутые в решении, я могу думать о выполнении этих операций в два этапа.

  1. Удалите начальные и конечные пробелы.
  2. Используйте find_first_of, find_last_of, find_first_not_of, find_last_not_of и substr несколько раз на границах слова, чтобы получить желаемое форматирование.
Анкур
источник

Ответы:

128

Это называется обрезкой. Если вы можете использовать Boost , я бы порекомендовал это.

В противном случае используйте find_first_not_ofдля получения индекса первого непробельного символа, а затем find_last_not_ofдля получения индекса с конца, который не является пробелом. С их помощью используйте, substrчтобы получить подстроку без окружающих пробелов.

В ответ на ваше изменение, я не знаю этого термина, но я бы предположил что-то вроде «уменьшить», так что я назвал это так. :) (Обратите внимание, я изменил пробел, чтобы он был параметром, для гибкости)

#include <iostream>
#include <string>

std::string trim(const std::string& str,
                 const std::string& whitespace = " \t")
{
    const auto strBegin = str.find_first_not_of(whitespace);
    if (strBegin == std::string::npos)
        return ""; // no content

    const auto strEnd = str.find_last_not_of(whitespace);
    const auto strRange = strEnd - strBegin + 1;

    return str.substr(strBegin, strRange);
}

std::string reduce(const std::string& str,
                   const std::string& fill = " ",
                   const std::string& whitespace = " \t")
{
    // trim first
    auto result = trim(str, whitespace);

    // replace sub ranges
    auto beginSpace = result.find_first_of(whitespace);
    while (beginSpace != std::string::npos)
    {
        const auto endSpace = result.find_first_not_of(whitespace, beginSpace);
        const auto range = endSpace - beginSpace;

        result.replace(beginSpace, range, fill);

        const auto newStart = beginSpace + fill.length();
        beginSpace = result.find_first_of(whitespace, newStart);
    }

    return result;
}

int main(void)
{
    const std::string foo = "    too much\t   \tspace\t\t\t  ";
    const std::string bar = "one\ntwo";

    std::cout << "[" << trim(foo) << "]" << std::endl;
    std::cout << "[" << reduce(foo) << "]" << std::endl;
    std::cout << "[" << reduce(foo, "-") << "]" << std::endl;

    std::cout << "[" << trim(bar) << "]" << std::endl;
}

Результат:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  
GManNickG
источник
Я предполагаю, что вы имели в виду size_t. и у вас есть поочередная подстрока, должно быть substr (beginStr, endStr - beginStr + 1);
goldPseudo
Должно site_tбыть size_t? И я думаю, что если у вас есть комментарий, это no whitespaceозначает, что строка пуста или пуста.
Фред Ларсон
Спасибо, исправил size_tопечатку и по очереди в редактировании, но не заметил, что мой комментарий был перевернут, спасибо.
GManNickG
@GMan очень элегантное решение. Спасибо.
Ankur
Ошибка: попробуйте запустить "one \ ttwo" через trim (). Результат - пустая строка. Вам также необходимо проверить endStr на std :: string :: npos.
dlchambers 03
48

Простое удаление начальных, конечных и лишних пробелов из std :: string в одной строке

value = std::regex_replace(value, std::regex("^ +| +$|( ) +"), "$1");

удаление только ведущих пробелов

value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to<char>(), ' ')));

или

value = std::regex_replace(value, std::regex("^ +"), "");

удаление только конечных пробелов

value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), value.end());

или

value = std::regex_replace(value, std::regex(" +$"), "");

удаление только лишних пробелов

value = regex_replace(value, std::regex(" +"), " ");
Евгений Карпов
источник
3
Хороший. Было бы полезно предоставить некоторую информацию о том, что здесь происходит, так как эти коды трудно понять.
Marcin
Однако работает только в C ++ 11.
Мартин Пека,
7
Вкладки не удаляются, но это можно исправить. Что нельзя исправить, так это то, что он ужасно медленный (примерно в 100 раз медленнее, чем ответы с помощью substrили erase).
4LegsDrivenCat
для оптимизации скорости регулярное выражение не является оптимальным решением, но его можно улучшить, создав экземпляр регулярного выражения один раз
Евгений Карпов
40

В настоящее время я использую эти функции:

// trim from left
inline std::string& ltrim(std::string& s, const char* t = " \t\n\r\f\v")
{
    s.erase(0, s.find_first_not_of(t));
    return s;
}

// trim from right
inline std::string& rtrim(std::string& s, const char* t = " \t\n\r\f\v")
{
    s.erase(s.find_last_not_of(t) + 1);
    return s;
}

// trim from left & right
inline std::string& trim(std::string& s, const char* t = " \t\n\r\f\v")
{
    return ltrim(rtrim(s, t), t);
}

// copying versions

inline std::string ltrim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
    return ltrim(s, t);
}

inline std::string rtrim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
    return rtrim(s, t);
}

inline std::string trim_copy(std::string s, const char* t = " \t\n\r\f\v")
{
    return trim(s, t);
}
Галик
источник
9

Это мое решение для удаления начальных и конечных пробелов ...

std::string stripString = "  Plamen     ";
while(!stripString.empty() && std::isspace(*stripString.begin()))
    stripString.erase(stripString.begin());

while(!stripString.empty() && std::isspace(*stripString.rbegin()))
    stripString.erase(stripString.length()-1);

Результат - «Пламен».

Пламен Стоянов
источник
8

Вот как это сделать:

std::string & trim(std::string & str)
{
   return ltrim(rtrim(str));
}

А вспомогательные функции реализуются как:

std::string & ltrim(std::string & str)
{
  auto it2 =  std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( str.begin() , it2);
  return str;   
}

std::string & rtrim(std::string & str)
{
  auto it1 =  std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( it1.base() , str.end() );
  return str;   
}

И как только вы все это разместите, вы также можете написать это:

std::string trim_copy(std::string const & str)
{
   auto s = str;
   return ltrim(rtrim(s));
}

Попробуй это

jha-G
источник
7

Пример обрезки начальных и конечных пробелов после предложения Джон-хансона использовать ускорение (удаляет только конечные и ожидающие пробелы):

#include <boost/algorithm/string/trim.hpp>

std::string str = "   t e s t    ";

boost::algorithm::trim ( str );

Результаты в "t e s t"

А также есть

  • trim_left приводит к "t e s t "
  • trim_right приводит к " t e s t"
Семьон Мёссингер
источник
5
/// strip a string, remove leading and trailing spaces
void strip(const string& in, string& out)
{
    string::const_iterator b = in.begin(), e = in.end();

    // skipping leading spaces
    while (isSpace(*b)){
        ++b;
    }

    if (b != e){
        // skipping trailing spaces
        while (isSpace(*(e-1))){
            --e;
        }
    }

    out.assign(b, e);
}

В приведенном выше коде функция isSpace () - это логическая функция, которая сообщает, является ли символ пробелом. Вы можете реализовать эту функцию, чтобы отразить свои потребности, или просто вызовите isspace () из «ctype.h», если хотите. .

Мерфи78
источник
4

Пример обрезки начальных и конечных пробелов

std::string aString("    This is a string to be trimmed   ");
auto start = aString.find_first_not_of(' ');
auto end = aString.find_last_not_of(' ');
std::string trimmedString;
trimmedString = aString.substr(start, (end - start) + 1);

ИЛИ

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);
Thinkal VB
источник
3
Людям не понравится изучать 10 страниц кода, чтобы научиться обрезать строку.
Thinkal VB
2
он не работает, если в строке есть только пробелы
DAG
3

Использование стандартной библиотеки имеет много преимуществ, но нужно знать о некоторых особых случаях, вызывающих исключения. Например, ни один из ответов не касается случая, когда строка C ++ содержит некоторые символы Unicode. В этом случае, если вы воспользуетесь функцией isspace , будет выдано исключение.

Я использовал следующий код для обрезки строк и некоторых других операций, которые могут пригодиться. Основные преимущества этого кода: он действительно быстрый (быстрее, чем любой код, который я когда-либо тестировал), он использует только стандартную библиотеку и никогда не вызывает исключения:

#include <string>
#include <algorithm>
#include <functional>
#include <locale>
#include <iostream>

typedef unsigned char BYTE;

std::string strTrim(std::string s, char option = 0)
{
    // convert all whitespace characters to a standard space
    std::replace_if(s.begin(), s.end(), (std::function<int(BYTE)>)::isspace, ' ');

    // remove leading and trailing spaces
    size_t f = s.find_first_not_of(' ');
    if (f == std::string::npos) return "";
    s = s.substr(f, s.find_last_not_of(' ') - f + 1);

    // remove consecutive spaces
    s = std::string(s.begin(), std::unique(s.begin(), s.end(),
        [](BYTE l, BYTE r){ return l == ' ' && r == ' '; }));

    switch (option)
    {
    case 'l':  // convert to lowercase
        std::transform(s.begin(), s.end(), s.begin(), ::tolower);
        return s;
    case 'U':  // convert to uppercase
        std::transform(s.begin(), s.end(), s.begin(), ::toupper);
        return s;
    case 'n':  // remove all spaces
        s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
        return s;
    default: // just trim
        return s;
    }
}
полфосол ఠ_ఠ
источник
3

Это может быть проще всего.

Вы можете использовать string::findи, string::rfindчтобы найти пробелы с обеих сторон и уменьшить строку.

void TrimWord(std::string& word)
{
    if (word.empty()) return;

    // Trim spaces from left side
    while (word.find(" ") == 0)
    {
        word.erase(0, 1);
    }

    // Trim spaces from right side
    size_t len = word.size();
    while (word.rfind(" ") == --len)
    {
        word.erase(len, len + 1);
    }
}
user2983960
источник
2

Я это тестировал, все работает. Таким образом, этот метод processInput просто попросит пользователя ввести что-то. Он вернет строку, в которой нет лишних пробелов внутри, а также лишних пробелов в начале или в конце. Надеюсь это поможет. (также поместите кучу комментариев, чтобы было проще понять).

вы можете увидеть, как это реализовать в main () внизу

#include <string>
#include <iostream>

string processInput() {
  char inputChar[256];
  string output = "";
  int outputLength = 0;
  bool space = false;
  // user inputs a string.. well a char array
  cin.getline(inputChar,256);
  output = inputChar;
       string outputToLower = "";
  // put characters to lower and reduce spaces
  for(int i = 0; i < output.length(); i++){
    // if it's caps put it to lowercase
    output[i] = tolower(output[i]);
    // make sure we do not include tabs or line returns or weird symbol for null entry array thingy
    if (output[i] != '\t' && output[i] != '\n' && output[i] != 'Ì') {
      if (space) {
        // if the previous space was a space but this one is not, then space now is false and add char
        if (output[i] != ' ') {
          space = false;
          // add the char
          outputToLower+=output[i];
        }
      } else {
        // if space is false, make it true if the char is a space
        if (output[i] == ' ') {
          space = true;
        }
        // add the char
        outputToLower+=output[i];
      }
    }
  }
  // trim leading and tailing space
  string trimmedOutput = "";
  for(int i = 0; i < outputToLower.length(); i++){
    // if it's the last character and it's not a space, then add it
    // if it's the first character and it's not a space, then add it
    // if it's not the first or the last then add it
    if (i == outputToLower.length() - 1 && outputToLower[i] != ' ' || 
      i == 0 && outputToLower[i] != ' ' || 
      i > 0 && i < outputToLower.length() - 1) {
      trimmedOutput += outputToLower[i];
    } 
  }
  // return
  output = trimmedOutput;
  return output;
}

int main() {
  cout << "Username: ";
  string userName = processInput();
  cout << "\nModified Input = " << userName << endl;
}
Элипсис
источник
2

Зачем усложнять?

std::string removeSpaces(std::string x){
    if(x[0] == ' ') { x.erase(0, 1); return removeSpaces(x); }
    if(x[x.length() - 1] == ' ') { x.erase(x.length() - 1, x.length()); return removeSpaces(x); }
    else return x;
}

Это работает, даже если ускорение не удалось, без регулярных выражений, без всяких странностей и библиотек.

РЕДАКТИРОВАТЬ: исправление для комментария MM.

Джек Клинков
источник
Это несколько неэффективно по сравнению с вычислением длины пробела и использованием одного вызова стирания для каждого конца
MM
1

В C ++ 17 представлен std::basic_string_viewшаблон класса, который ссылается на постоянную непрерывную последовательность char-подобных объектов, то есть представление строки. Помимо очень похожего интерфейса std::basic_string, у него есть две дополнительные функции:, remove_prefix()которая сжимает представление, перемещая его начало вперед; и remove_suffix(), который сжимает изображение, перемещая его конец назад. Их можно использовать для обрезки начального и конечного пробелов:

#include <string_view>
#include <string>

std::string_view ltrim(std::string_view str)
{
    const auto pos(str.find_first_not_of(" \t"));
    str.remove_prefix(pos);
    return str;
}

std::string_view rtrim(std::string_view str)
{
    const auto pos(str.find_last_not_of(" \t"));
    str.remove_suffix(str.length() - pos - 1);
    return str;
}

std::string_view trim(std::string_view str)
{
    str = ltrim(str);
    str = rtrim(str);
    return str;
}

int main()
{
    std::string str = "   hello world   ";
    auto sv1{ ltrim(str) };  // "hello world   "
    auto sv2{ rtrim(str) };  // "   hello world"
    auto sv3{ trim(str) };   // "hello world"

    //If you want, you can create std::string objects from std::string_view objects
    auto s1{ sv1 };
    auto s2{ sv2 };
    auto s3{ sv3 };
}

Примечание: std::string_viewссылка не принадлежит владельцу, поэтому она действительна только до тех пор, пока существует исходная строка.

jignatius
источник
0
    char *str = (char*) malloc(50 * sizeof(char));
    strcpy(str, "    some random string (<50 chars)  ");

    while(*str == ' ' || *str == '\t' || *str == '\n')
            str++;

    int len = strlen(str);

    while(len >= 0 && 
            (str[len - 1] == ' ' || str[len - 1] == '\t' || *str == '\n')
    {
            *(str + len - 1) = '\0';
            len--;
    }

    printf(":%s:\n", str);
Амаргош
источник
0
void removeSpaces(string& str)
{
    /* remove multiple spaces */
    int k=0;
    for (int j=0; j<str.size(); ++j)
    {
            if ( (str[j] != ' ') || (str[j] == ' ' && str[j+1] != ' ' ))
            {
                    str [k] = str [j];
                    ++k;
            }

    }
    str.resize(k);

    /* remove space at the end */   
    if (str [k-1] == ' ')
            str.erase(str.end()-1);
    /* remove space at the begin */
    if (str [0] == ' ')
            str.erase(str.begin());
}
Девеш Агравал
источник
0
string trim(const string & sStr)
{
    int nSize = sStr.size();
    int nSPos = 0, nEPos = 1, i;
    for(i = 0; i< nSize; ++i) {
        if( !isspace( sStr[i] ) ) {
            nSPos = i ;
            break;
        }
    }
    for(i = nSize -1 ; i >= 0 ; --i) {
        if( !isspace( sStr[i] ) ) {
            nEPos = i;
            break;
        }
    }
    return string(sStr, nSPos, nEPos - nSPos + 1);
}
kjk
источник
0

Как насчет начальных и конечных пробелов:

string string_trim(const string& in) {

    stringstream ss;
    string out;
    ss << in;
    ss >> out;
    return out;

}

Или для предложения:

string trim_words(const string& sentence) {
    stringstream ss;
    ss << sentence;
    string s;
    string out;

    while(ss >> s) {

        out+=(s+' ');
    }
    return out.substr(0, out.length()-1);
}
Iderwok
источник
0

опрятный и чистый

 void trimLeftTrailingSpaces(string &input) {
        input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
            return !isspace(ch);
        }));
    }

    void trimRightTrailingSpaces(string &input) {
        input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
            return !isspace(ch);
        }).base(), input.end());
    }
user1856722
источник
0

Нет- boostнет regex, просто stringбиблиотека. Это так просто.

string trim(const string s) { // removes whitespace characters from beginnig and end of string s
    const int l = (int)s.length();
    int a=0, b=l-1;
    char c;
    while(a<l && ((c=s.at(a))==' '||c=='\t'||c=='\n'||c=='\v'||c=='\f'||c=='\r'||c=='\0')) a++;
    while(b>a && ((c=s.at(b))==' '||c=='\t'||c=='\n'||c=='\v'||c=='\f'||c=='\r'||c=='\0')) b--;
    return s.substr(a, 1+b-a);
}
ProjectPhysX
источник
1
... и вы избежали добавления 2M файлов заголовков в свою сборку!
Larry_C
0

Чтобы усугубить проблему, как расширить это форматирование для обработки лишних пробелов между словами строки.

На самом деле это более простой случай, чем учет нескольких начальных и конечных символов пробела. Все, что вам нужно сделать, это удалить повторяющиеся соседние символы пробела из всей строки.

Предикат для соседнего пробела будет просто:

auto by_space = [](unsigned char a, unsigned char b) {
    return std::isspace(a) and std::isspace(b);
};

а затем вы можете избавиться от этих повторяющихся соседних символов пробела с std::uniqueпомощью идиомы стирания-удаления:

// s = "       This       is       a sample   string     "  
s.erase(std::unique(std::begin(s), std::end(s), by_space), 
        std::end(s));
// s = " This is a sample string "  

Это потенциально оставляет лишний символ пробела спереди и / или сзади. Это можно довольно легко удалить:

if (std::size(s) && std::isspace(s.back()))
    s.pop_back();

if (std::size(s) && std::isspace(s.front()))
    s.erase(0, 1);

Вот демо .

cigien
источник
-1

Мое решение этой проблемы без использования каких-либо методов STL, а только с использованием собственных методов строки C ++, выглядит следующим образом :

void processString(string &s) {
    if ( s.empty() ) return;

    //delete leading and trailing spaces of the input string
    int notSpaceStartPos = 0, notSpaceEndPos = s.length() - 1;
    while ( s[notSpaceStartPos] == ' ' ) ++notSpaceStartPos;
    while ( s[notSpaceEndPos] == ' ' ) --notSpaceEndPos;
    if ( notSpaceStartPos > notSpaceEndPos ) { s = ""; return; }
    s = s.substr(notSpaceStartPos, notSpaceEndPos - notSpaceStartPos + 1);

    //reduce multiple spaces between two words to a single space 
    string temp;
    for ( int i = 0; i < s.length(); i++ ) {
        if ( i > 0 && s[i] == ' ' && s[i-1] == ' ' ) continue;
        temp.push_back(s[i]);
    }
    s = temp;
}

Я использовал этот метод для передачи проблемы LeetCode Обратные слова в строке

Чарльз Ван
источник
-1
void TrimWhitespaces(std::wstring& str)
{
    if (str.empty())
        return;

    const std::wstring& whitespace = L" \t";
    std::wstring::size_type strBegin = str.find_first_not_of(whitespace);
    std::wstring::size_type strEnd = str.find_last_not_of(whitespace);

    if (strBegin != std::wstring::npos || strEnd != std::wstring::npos)
    {
        strBegin == std::wstring::npos ? 0 : strBegin;
        strEnd == std::wstring::npos ? str.size() : 0;

        const auto strRange = strEnd - strBegin + 1;
        str.substr(strBegin, strRange).swap(str);
    }
    else if (str[0] == ' ' || str[0] == '\t')   // handles non-empty spaces-only or tabs-only
    {
        str = L"";
    }
}

void TrimWhitespacesTest()
{
    std::wstring EmptyStr = L"";
    std::wstring SpacesOnlyStr = L"    ";
    std::wstring TabsOnlyStr = L"           ";
    std::wstring RightSpacesStr = L"12345     ";
    std::wstring LeftSpacesStr = L"     12345";
    std::wstring NoSpacesStr = L"12345";

    TrimWhitespaces(EmptyStr);
    TrimWhitespaces(SpacesOnlyStr);
    TrimWhitespaces(TabsOnlyStr);
    TrimWhitespaces(RightSpacesStr);
    TrimWhitespaces(LeftSpacesStr);
    TrimWhitespaces(NoSpacesStr);

    assert(EmptyStr == L"");
    assert(SpacesOnlyStr == L"");
    assert(TabsOnlyStr == L"");
    assert(RightSpacesStr == L"12345");
    assert(LeftSpacesStr == L"12345");
    assert(NoSpacesStr == L"12345");
}
Иван Стрелец
источник
-2

А как насчет идиомы « стереть-удалить» ?

std::string s("...");
s.erase( std::remove(s.begin(), s.end(), ' '), s.end() );

Сожалею. Я слишком поздно понял, что вы не хотите удалять все пробелы.

vt.
источник
Привет, теперь, когда вы знаете, что ответ неправильный, вы можете удалить его, если хотите. Таким образом, вы получите репутацию, которую вы потеряли из-за этого ответа от DV :)
cigien