¿Cómo recortar un std::string?

7 minutos de lectura

Avatar de usuario de Milan Babuškov
Milán Babuškov

Actualmente estoy usando el siguiente código para recortar a la derecha todos los std::strings en mis programas:

std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);

Funciona bien, pero me pregunto si hay algunos casos finales en los que podría fallar.

Por supuesto, las respuestas con alternativas elegantes y también con la solución de ajuste a la izquierda son bienvenidas.

Avatar de usuario de Leon Timmermans
leon timmermans

Usando Algoritmos de cadena de Boost sería más fácil:

#include <boost/algorithm/string.hpp>

std::string str("hello world! ");
boost::trim_right(str);

str es ahora "hello world!". También hay trim_left y trimque recorta ambos lados.


si agregas _copy sufijo a cualquiera de los nombres de funciones anteriores, por ejemplo trim_copyla función devolverá una copia recortada de la cadena en lugar de modificarla a través de una referencia.

si agregas _if sufijo a cualquiera de los nombres de funciones anteriores, por ejemplo trim_copy_ifpuede recortar todos los caracteres que satisfagan su predicado personalizado, a diferencia de solo los espacios en blanco.

  • ¿Qué utiliza boost para determinar si un carácter es un espacio en blanco?

    – Tomás

    7 de diciembre de 2008 a las 21:22

  • Depende del lugar. Mi configuración regional predeterminada (VS2005, en) significa que se recortan las pestañas, los espacios, los retornos de carro, las líneas nuevas, las pestañas verticales y los avances de formulario.

    – MattyT

    26 de enero de 2009 a las 13:11

  • Ya estoy usando mucho impulso, #include <boost/format.hpp> #include <boost/tokenizer.hpp> #include <boost/lexical_cast.hpp> pero estaba preocupado por el exceso de código para agregar <boost/algorithm/string.hpp> cuando ya hay std::string::erase alternativas basadas. Feliz de informar al comparar compilaciones de MinSizeRel antes y después de agregarlo, ese recorte de impulso no aumentó mi tamaño de código en absoluto (ya debe estar pagando por él en alguna parte) y mi código no está abarrotado con algunas funciones más.

    –Rian Sanderson

    25 de julio de 2011 a las 5:36


  • @MattyT: ¿Qué referencia está usando para esta lista (determinando si un carácter es un espacio en blanco)?

    – Fahim Mitha

    30 de diciembre de 2011 a las 4:04

  • realmente no responde la pregunta que solicita std::string (no para boost ni ninguna otra biblioteca …)

    – hfrmóvil

    4 de noviembre de 2019 a las 14:20

  • si usa basic_string y template en el CharT, puede hacer esto para todas las cadenas, solo use una variable de plantilla para el espacio en blanco para que lo use como ws. técnicamente, en ese momento, podría prepararlo para c ++ 20 y marcarlo constexpr también, ya que esto implica en línea

    – varado

    2 de diciembre de 2019 a las 1:09

  • @Beached de hecho. Sin embargo, es un poco complicado poner una respuesta aquí. He escrito funciones de plantilla para esto y ciertamente es bastante complicado. He probado un montón de enfoques diferentes y todavía no estoy seguro de cuál es el mejor.

    – Galik

    2 de diciembre de 2019 a las 1:18

Avatar de usuario de Bill the Lizard
Bill el lagarto

Use el siguiente código para recortar a la derecha (finales) los espacios y los caracteres de tabulación de std::strings (idea):

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

Y solo para equilibrar las cosas, también incluiré el código de recorte izquierdo (idea):

// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )
{
    str = str.substr( startpos );
}

  • Esto no detectará otras formas de espacios en blanco… nueva línea, avance de línea, retorno de carro en particular.

    – Tomás

    7 de diciembre de 2008 a las 21:23

  • Derecha. Debe personalizarlo para el espacio en blanco que desea recortar. Mi aplicación particular solo esperaba espacios y tabulaciones, pero puede agregar \n\r para atrapar a los demás.

    – Bill el lagarto

    8 de diciembre de 2008 a las 0:20

  • str.substr(...).swap(str) es mejor. Guardar una tarea.

    – updogliu

    30 de agosto de 2012 a las 8:55


  • @updogliu ¿No usará la asignación de movimiento? basic_string& operator= (basic_string&& str) noexcept; ?

    – nurettina

    8 de octubre de 2013 a las 8:47


  • Esta respuesta no altera las cadenas que son TODOS espacios. Que es un fail.

    –Tom Andersen

    09/04/2014 a las 22:19

Avatar de usuario de Jeromy Adofo
jeromey adofo

Prueba esto, me funciona.

inline std::string trim(std::string& str)
{
    str.erase(str.find_last_not_of(' ')+1);         //suffixing spaces
    str.erase(0, str.find_first_not_of(' '));       //prefixing spaces
    return str;
}

  • Esto no detectará otras formas de espacios en blanco… nueva línea, avance de línea, retorno de carro en particular.

    – Tomás

    7 de diciembre de 2008 a las 21:23

  • Derecha. Debe personalizarlo para el espacio en blanco que desea recortar. Mi aplicación particular solo esperaba espacios y tabulaciones, pero puede agregar \n\r para atrapar a los demás.

    – Bill el lagarto

    8 de diciembre de 2008 a las 0:20

  • str.substr(...).swap(str) es mejor. Guardar una tarea.

    – updogliu

    30 de agosto de 2012 a las 8:55


  • @updogliu ¿No usará la asignación de movimiento? basic_string& operator= (basic_string&& str) noexcept; ?

    – nurettina

    8 de octubre de 2013 a las 8:47


  • Esta respuesta no altera las cadenas que son TODOS espacios. Que es un fail.

    –Tom Andersen

    09/04/2014 a las 22:19

Un poco tarde para la fiesta, pero no importa. Ahora C++11 está aquí, tenemos lambdas y variables automáticas. Entonces mi versión, que también maneja espacios en blanco y cadenas vacías, es:

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

Podríamos hacer un iterador inverso de wsfront y usar eso como la condición de terminación en el segundo find_if_not pero eso solo es útil en el caso de una cadena de espacios en blanco, y gcc 4.8 al menos no es lo suficientemente inteligente como para inferir el tipo del iterador inverso (std::string::const_reverse_iterator) con auto. No sé qué tan costoso es construir un iterador inverso, así que YMMV aquí. Con esta alteración, el código se ve así:

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

  • Siempre quiero una llamada de función para recortar la cadena, en lugar de implementarla

    – linquizar

    17 de enero de 2014 a las 8:56

  • Por lo que vale, no hay necesidad de usar ese lambda. solo puedes pasar std::isspace: auto wsfront=std::find_if_not(s.begin(),s.end(),std::isspace);

    – vmrob

    10 mayo 2014 a las 22:52

  • Los compiladores de @vmrob no son necesariamente tan inteligentes. haciendo lo que dices es ambiguo: candidate template ignored: couldn't infer template argument '_Predicate' find_if_not(_InputIterator __first, _InputIterator __last, _Predicate __pred)

    – johnbakers

    21 dic 2016 a las 15:50

  • @vmrob No, no puedes. isspace tiene dos sobrecargas. Además, tomar la dirección de una función en la biblioteca estándar es UB desde C++20.

    – LF

    22 de julio de 2019 a las 13:17

  • @vmrob la otra sobrecarga es la que toma una configuración regional. ::isspace Sin embargo, lo haría antes de C ++ 20 (siempre que incluya el encabezado C). En realidad, un problema adicional es que el argumento debe convertirse en char sin firmar antes de enviarse a isspace, pero esa es otra historia.

    – LF

    18 de septiembre de 2020 a las 10:16

¿Ha sido útil esta solución?