Eliminar espacios iniciales y finales de una cadena

92

Cómo eliminar espacios de un objeto de cadena en C ++.
Por ejemplo, cómo eliminar los espacios iniciales y finales del objeto de cadena siguiente.

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

La clase de cadena, hasta donde yo sé, no proporciona ningún método para eliminar los espacios iniciales y finales.

Para aumentar el problema, cómo extender este formato para procesar espacios adicionales entre palabras de la cadena. Por ejemplo,

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

Usando los métodos de cadena mencionados en la solución, puedo pensar en hacer estas operaciones en dos pasos.

  1. Elimine los espacios iniciales y finales.
  2. Utilice find_first_of, find_last_of, find_first_not_of, find_last_not_of y substr , repetidamente en los límites de palabras para obtener el formato deseado.
Ankur
fuente

Respuestas:

128

Esto se llama recorte. Si puede usar Boost , lo recomiendo.

De lo contrario, use find_first_not_ofpara obtener el índice del primer carácter que no sea un espacio en blanco, luego find_last_not_ofpara obtener el índice del final que no es un espacio en blanco. Con estos, use substrpara obtener la subcadena sin espacios en blanco circundantes.

En respuesta a su edición, no conozco el término, pero supongo que algo así como "reducir", así es como lo llamé. :) (Nota, he cambiado el espacio en blanco para que sea un parámetro, por flexibilidad)

#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
fuente
Supongo que te refieres a 'size_t'. y tiene un off-by-one en la subcadena, debería ser substr (beginStr, endStr - beginStr + 1);
goldPseudo
¿Debería site_tser size_t? Y creo que donde tienes el comentario no whitespacesignifica que la cadena está en blanco o vacía.
Fred Larson
Gracias, se corrigió el size_terror tipográfico y uno por uno en la edición, pero no noté que mi comentario estaba invertido, gracias.
GManNickG
@GMan solución muy elegante. Gracias.
Ankur
Error: intente ejecutar "uno \ ttwo" a través de trim (). El resultado es una cadena vacía. También debe probar endStr contra std :: string :: npos.
dlchambers
48

Elimina fácilmente los espacios iniciales, finales y adicionales de una cadena std :: en una línea

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

eliminar solo los espacios iniciales

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

o

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

eliminar solo los espacios finales

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

o

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

eliminando solo espacios adicionales

value = regex_replace(value, std::regex(" +"), " ");
Evgeny Karpov
fuente
3
Buena esa. Sería útil proporcionar información sobre lo que está sucediendo aquí, ya que es difícil entender estos códigos.
Marcin
Sin embargo, solo funciona en C ++ 11.
Martin Pecka
7
No elimina las pestañas, pero esto se puede solucionar. Lo que no se puede arreglar es que es terriblemente lento (~ 100 veces más lento que las respuestas con substro erase).
4LegsDrivenCat
para la optimización de la velocidad, la expresión regular no es la solución óptima, pero podría mejorarse creando una instancia de expresión regular una vez
Evgeny Karpov
40

Actualmente estoy usando estas funciones:

// 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
fuente
9

Esta es mi solución para eliminar los espacios iniciales y finales ...

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

El resultado es "Plamen"

Plamen Stoyanov
fuente
8

Aquí sabrás como podrás hacerlo:

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

Y las funciones de apoyo se implementan 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;   
}

Y una vez que tenga todo esto en su lugar, puede escribir esto también:

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

Prueba esto

jha-G
fuente
7

Ejemplo para recortar espacios iniciales y finales siguiendo la sugerencia de jon-hanson de usar boost (solo elimina los espacios finales y pendientes):

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

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

boost::algorithm::trim ( str );

Resultados en "t e s t"

También hay

  • trim_left resultados en "t e s t "
  • trim_right resultados en " t e s t"
Semjon Mössinger
fuente
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);
}

En el código anterior, la función isSpace () es una función booleana que indica si un carácter es un espacio en blanco, puede implementar esta función para reflejar sus necesidades, o simplemente llamar a isspace () desde "ctype.h" si lo desea .

Murphy78
fuente
4

Ejemplo para recortar los espacios iniciales y finales

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

O

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);
Thinkal VB
fuente
3
A la gente no le gustará mirar 10 páginas de código para aprender a recortar una cuerda.
Thinkal VB
2
está roto si la cadena solo tiene espacios
DAG
3

El uso de la biblioteca estándar tiene muchas ventajas, pero hay que tener en cuenta algunos casos especiales que provocan excepciones. Por ejemplo, ninguna de las respuestas cubrió el caso en el que una cadena C ++ tiene algunos caracteres Unicode. En este caso, si usa la función isspace , se lanzará una excepción.

He estado usando el siguiente código para recortar las cadenas y algunas otras operaciones que pueden resultar útiles. Los principales beneficios de este código son: es realmente rápido (más rápido que cualquier código que haya probado), solo usa la biblioteca estándar y nunca causa una excepción:

#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 ఠ_ఠ
fuente
3

Este podría ser el más simple de todos.

Puede usar string::findy string::rfindpara buscar espacios en blanco de ambos lados y reducir la cadena.

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

He probado esto, todo funciona. Entonces, este método processInput solo le pedirá al usuario que escriba algo. Devolverá una cadena que no tiene espacios adicionales internamente, ni espacios adicionales al principio o al final. Espero que esto ayude. (también agregue un montón de comentarios para que sea fácil de entender).

puedes ver cómo implementarlo en main () en la 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
fuente
2

¿Por qué 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;
}

Esto funciona incluso si el impulso fallara, sin expresiones regulares, sin cosas raras ni bibliotecas.

EDITAR: Corrección para el comentario de MM.

Jack de cuchillas
fuente
Esto es algo ineficiente, en comparación con el cálculo de la longitud del espacio en blanco y el uso de una sola llamada de borrado para cada extremo
MM
1

Se introdujo C ++ 17 std::basic_string_view, una plantilla de clase que se refiere a una secuencia contigua constante de objetos tipo char, es decir, una vista de la cadena. Además de tener una interfaz muy similar a std::basic_string, tiene dos funciones adicionales:, remove_prefix()que encoge la vista moviendo su inicio hacia adelante; y remove_suffix(), que encoge la vista moviendo su extremo hacia atrás. Estos se pueden usar para recortar el espacio inicial y 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 };
}

Nota: std::string_viewes una referencia no propietaria, por lo que solo es válida mientras la cadena original aún exista.

jignatius
fuente
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
fuente
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
fuente
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
fuente
0

Para espacios iniciales y finales, ¿qué tal:

string string_trim(const string& in) {

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

}

O para una oración:

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

limpio y ordenado

 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());
    }
usuario1856722
fuente
0

No boost, no regex, solo la stringbiblioteca. Es así de simple.

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);
}
ProyectoPhysX
fuente
1
... ¡y evitó incorporar 2M de archivos de encabezado en su compilación!
Larry_C
0

Para aumentar el problema, cómo extender este formato para procesar espacios adicionales entre palabras de la cadena.

En realidad, este es un caso más simple que tener en cuenta varios caracteres de espacios en blanco iniciales y finales. Todo lo que necesita hacer es eliminar los caracteres de espacio en blanco adyacentes duplicados de toda la cadena.

El predicado para el espacio en blanco adyacente sería simplemente:

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

y luego puede deshacerse de esos caracteres de espacio en blanco adyacentes duplicados con std::unique, y el modismo borrar-eliminar:

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

Esto potencialmente deja un carácter de espacio en blanco adicional en la parte delantera y / o trasera. Esto se puede eliminar con bastante facilidad:

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

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

Aquí tienes una demostración .

cigien
fuente
-1

Mi solución para este problema que no utiliza ningún método STL sino solo los métodos propios de la cadena C ++ es la siguiente:

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

He usado este método para pasar un problema de LeetCode Palabras inversas en una cadena

Charles Wang
fuente
-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
fuente
-2

¿Qué pasa con el modismo borrar-eliminar ?

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

Lo siento. Vi demasiado tarde que no quieres quitar todo espacios en blanco.

Vermont.
fuente
Hola, ahora que sabes que la respuesta es incorrecta, puedes borrarla si quieres. De esa manera, recuperará el representante que perdió de los DV en esta respuesta :)
cigien