Remover espaços à esquerda e à direita de uma string

95

Como remover espaços de um objeto string em C ++.
Por exemplo, como remover espaços iniciais e finais do objeto string abaixo.

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

A classe string, até onde eu sei, não fornece nenhum método para remover espaços à esquerda e à direita.

Para aumentar o problema, como estender essa formatação para processar espaços extras entre palavras da string. Por exemplo,

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

Usando os métodos de string mencionados na solução, posso pensar em fazer essas operações em duas etapas.

  1. Remova os espaços à esquerda e à direita.
  2. Use find_first_of, find_last_of, find_first_not_of, find_last_not_of e substr , repetidamente nos limites da palavra para obter a formatação desejada.
Ankur
fonte

Respostas:

129

Isso é chamado de corte. Se você pode usar Boost , eu recomendo.

Caso contrário, use find_first_not_ofpara obter o índice do primeiro caractere sem espaço em branco e, a seguir, find_last_not_ofpara obter o índice do final que não é um espaço em branco. Com eles, use substrpara obter a substring sem nenhum espaço em branco ao redor.

Em resposta à sua edição, não sei o termo, mas acho que algo na linha de "reduzir", é assim que chamei. :) (Observe, alterei o espaço em branco para ser um parâmetro, para flexibilidade)

#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;
}

Resultado:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  
GManNickG
fonte
eu suponho que você quis dizer 'size_t'. e você tem um off-por-um na substring, deve ser substr (beginStr, endStr - beginStr + 1);
goldPseudo
Deve site_tser size_t? E eu acho que onde você tem o comentário no whitespacesignifica que a string está toda em branco ou vazia.
Fred Larson
Obrigado, resolvi o size_terro de digitação e erro na edição, mas não percebi que meu comentário estava invertido, obrigado.
GManNickG
@GMan solução muito elegante. Obrigado.
Ankur
Bug: tente executar "um \ ttwo" através de trim (). O resultado é uma string vazia. Você também precisa testar endStr em relação a std :: string :: npos.
dlchambers
48

Fácil remoção de espaços iniciais, finais e extras de um std :: string em uma linha

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

removendo apenas espaços iniciais

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

ou

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

removendo apenas espaços finais

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

ou

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

removendo apenas espaços extras

value = regex_replace(value, std::regex(" +"), " ");
Evgeny Karpov
fonte
3
Agradável. Seria útil fornecer algumas informações sobre o que está acontecendo aqui, pois é difícil entender esses códigos.
Marcin,
Funciona apenas em C ++ 11, entretanto.
Martin Pecka
7
Ele não remove as guias, mas isso pode ser corrigido. O que não pode ser corrigido é que ele é terrivelmente lento (aproximadamente 100 vezes mais lento do que respostas com substrou erase).
4LegsDrivenCat
para otimização de velocidade, o regex não é a solução ideal, mas pode ser melhorado criando uma instância de regex uma vez
Evgeny Karpov,
40

Atualmente, estou usando estas funções:

// 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);
}
Galik
fonte
22

Algoritmo de corte de string de reforço

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

[...]

std::string msg = "   some text  with spaces  ";
boost::algorithm::trim(msg);
Jon-Hanson
fonte
9

Esta é a minha solução para eliminar os espaços à esquerda e à direita ...

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);

O resultado é "Plamen"

Plamen Stoyanov
fonte
8

Aqui está como você pode fazer isso:

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

E as funções de suporte são implementadas como:

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;   
}

E depois de ter tudo isso no lugar, você também pode escrever isto:

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

Tente isto

jha-G
fonte
7

Exemplo para cortar espaços à esquerda e à direita seguindo a sugestão de Jon-Hanson para usar boost (remove apenas espaços à direita e pendentes):

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

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

boost::algorithm::trim ( str );

Resulta em "t e s t"

Também há

  • trim_left resulta em "t e s t "
  • trim_right resulta em " t e s t"
Semjon Mössinger
fonte
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);
}

No código acima, a função isSpace () é uma função booleana que diz se um caractere é um espaço em branco, você pode implementar esta função para refletir suas necessidades ou apenas chamar o isspace () de "ctype.h" se desejar .

Murphy78
fonte
4

Exemplo para cortar espaços à esquerda e à direita

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);

OU

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);
Thinkal VB
fonte
3
Os povos não gostam de olhar 10 páginas de código para aprender como cortar uma string.
Thinkal VB de
2
será quebrado se a string tiver apenas espaços
DAG
3

Usar a biblioteca padrão tem muitos benefícios, mas deve-se estar ciente de alguns casos especiais que causam exceções. Por exemplo, nenhuma das respostas cobriu o caso em que uma string C ++ tem alguns caracteres Unicode. Nesse caso, se você usar a função isspace , uma exceção será lançada.

Tenho usado o seguinte código para aparar as cordas e algumas outras operações que podem ser úteis. Os principais benefícios desse código são: ele é muito rápido (mais rápido do que qualquer código que já testei), ele usa apenas a biblioteca padrão e nunca causa uma exceção:

#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;
    }
}
polfosol ఠ_ఠ
fonte
3

Isso pode ser o mais simples de todos.

Você pode usar string::finde string::rfindpara localizar espaços em branco de ambos os lados e reduzir a string.

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
fonte
2

Eu testei isso, tudo funciona. Portanto, este método processInput pedirá apenas que o usuário digite algo. Ele retornará uma string que não possui espaços extras internamente, nem espaços extras no início ou no final. Espero que isto ajude. (também coloque muitos comentários para torná-lo simples de entender).

você pode ver como implementá-lo no main () na parte inferior

#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;
}
Elipsis
fonte
2

Por que complicar?

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;
}

Isso funciona mesmo se o boost falhar, sem regex, sem coisas estranhas ou bibliotecas.

EDIT: Correção para o comentário de MM.

Jack Of Blades
fonte
Isso é um tanto ineficiente, em comparação com o cálculo do comprimento do espaço em branco e usando uma única chamada de apagamento para cada extremidade
MM
1

C ++ 17 introduzido std::basic_string_view, um modelo de classe que se refere a uma sequência contígua constante de objetos semelhantes a char, ou seja, uma visualização da string. Além de ter uma interface muito semelhante ao std::basic_string, tem duas funções adicionais remove_prefix():, que reduz a visualização movendo o início para a frente; e remove_suffix(), o que reduz a visão movendo sua extremidade para trás. Eles podem ser usados ​​para cortar o espaço inicial e final:

#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 };
}

Observação: o std::string_viewé uma referência não proprietária, portanto, só é válido enquanto a string original ainda existir.

Jignatius
fonte
1

Para aumentar o problema, como estender essa formatação para processar espaços extras entre palavras da string.

Na verdade, este é um caso mais simples do que considerar vários caracteres de espaço em branco à esquerda e à direita. Tudo o que você precisa fazer é remover caracteres de espaço em branco adjacentes duplicados de toda a string.

O predicado para o espaço em branco adjacente seria simplesmente:

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

e, em seguida, você pode se livrar desses caracteres de espaço em branco adjacentes duplicados com std::uniquee o idioma apagar-remover:

// 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 "  

Isso potencialmente deixa um caractere de espaço em branco extra na frente e / ou atrás. Isso pode ser removido facilmente:

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

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

Aqui está uma demonstração .

cigien
fonte
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);
Amarghosh
fonte
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());
}
Devesh Agrawal
fonte
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
fonte
0

Para espaços à esquerda e à direita, que tal:

string string_trim(const string& in) {

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

}

Ou para uma frase:

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
fonte
0

arrumado e limpo

 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
fonte
0

Não boost, não regex, apenas a stringbiblioteca. É simples assim.

ProjectPhysX
fonte
1
... e você evitou trazer 2M de arquivos de cabeçalho para a sua construção!
Larry_C
-1

Minha solução para este problema não usando nenhum método STL, mas apenas os próprios métodos da string C ++ é a seguinte:

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;
}

Eu usei este método para passar um problema LeetCode Reverter palavras em uma string

Charles Wang
fonte
-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");
}
Ivan Strelets
fonte
-2

E quanto ao idioma apagar-remover ?

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

Desculpa. Eu vi tarde demais que você não deseja remover todos os espaços em branco.

vt.
fonte
Olá, agora que você sabe que a resposta está errada, você pode excluí-la se quiser. Dessa forma, você terá de volta o representante que perdeu dos DVs nesta resposta :)
cigien