web-dev-qa-db-fra.com

Quelle est la meilleure façon de couper std :: string?

J'utilise actuellement le code suivant pour redimensionner à droite tout le std::strings dans mes programmes:

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

Cela fonctionne bien, mais je me demande s’il existe des cas où cela pourrait échouer.

Bien sûr, les réponses avec des alternatives élégantes ainsi qu'une solution à gauche sont les bienvenues.

696
Milan Babuškov

EDIT Depuis c ++ 17, certaines parties de la bibliothèque standard ont été supprimées. Heureusement, à partir de c ++ 11, nous avons des lambdas qui constituent une solution supérieure.

#include <algorithm> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

Merci à https://stackoverflow.com/a/44973498/524503 d’avoir mis en place la solution moderne.

Réponse originale:

J'ai tendance à utiliser l'un de ces 3 pour mes besoins de coupe:

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>

// trim from start
static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

// trim from end
static inline std::string &rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
}

// trim from both ends
static inline std::string &trim(std::string &s) {
    return ltrim(rtrim(s));
}

Ils sont assez explicites et fonctionnent très bien.

EDIT: En passant, j'ai std::ptr_fun dedans pour aider à lever l'ambiguïté std::isspace car il y a en fait une deuxième définition qui supporte les locales. Cela aurait pu être un casting de la même façon, mais j'ai tendance à aimer ça mieux.

EDIT: Pour adresser quelques commentaires sur l'acceptation d'un paramètre par référence, sa modification et son renvoi. Je suis d'accord. Une implémentation que je préférerais probablement serait deux ensembles de fonctions, l’une pour in situ et l’autre pour la copie. Un meilleur ensemble d'exemples serait:

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

Je garde la réponse originale ci-dessus, bien que pour le contexte et dans l’intérêt de garder la réponse avec vote élevé encore disponible.

568
Evan Teran

Utiliser les algorithmes de chaîne de Boost serait le plus simple:

#include <boost/algorithm/string.hpp>

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

str est maintenant "hello world!". Il y a aussi trim_left et trim, qui coupe les deux côtés.


Si vous ajoutez le suffixe _copy à l’un des noms de fonction ci-dessus, par exemple: trim_copy, la fonction retournera une copie découpée de la chaîne au lieu de la modifier via une référence.

Si vous ajoutez le suffixe _if à l’un des noms de fonction ci-dessus, par exemple: trim_copy_if, vous pouvez supprimer tous les caractères correspondant à votre prédicat personnalisé, par opposition aux espaces.

391
Leon Timmermans

Utilisez le code suivant pour découper à droite les espaces et les caractères de tabulation à partir de std::strings ( ideone ):

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

Et juste pour équilibrer les choses, j'inclurai aussi le code de rognage de gauche ( ideone ):

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

Peu en retard à la fête, mais peu importe. Maintenant que C++ 11 est arrivé, nous avons lambdas et auto variables. Ainsi, ma version, qui gère également les chaînes entièrement blanches et vides, est la suivante:

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

Nous pourrions créer un itérateur inverse à partir de wsfront et l'utiliser comme condition de terminaison dans le second find_if_not, mais cela n'est utile que dans le cas d'une chaîne contenant uniquement des espaces, et gcc 4.8 au moins n'est pas assez intelligent pour déduire le type de l'inverse. itérateur (std::string::const_reverse_iterator) avec auto. Je ne sais pas combien coûte la construction d'un itérateur inversé, donc YMMV ici. Avec cette modification, le code ressemble à ceci:

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

Ce que vous faites est bien et robuste. J'ai utilisé la même méthode depuis longtemps et je n'ai pas encore trouvé de méthode plus rapide:

const char* ws = " \t\n\r\f\v";

// trim from end of string (right)
inline std::string& rtrim(std::string& s, const char* t = ws)
{
    s.erase(s.find_last_not_of(t) + 1);
    return s;
}

// trim from beginning of string (left)
inline std::string& ltrim(std::string& s, const char* t = ws)
{
    s.erase(0, s.find_first_not_of(t));
    return s;
}

// trim from both ends of string (right then left)
inline std::string& trim(std::string& s, const char* t = ws)
{
    return ltrim(rtrim(s, t), t);
}

En fournissant les caractères à découper, vous avez la possibilité de supprimer les caractères non blancs et l'efficacité de supprimer uniquement les caractères souhaités.

38
Galik

Essayez ceci, ça marche pour moi.

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

J'aime la solution de tzaman, le seul problème est qu'elle ne coupe pas une chaîne contenant uniquement des espaces.

Pour corriger cette faille, ajoutez un str.clear () entre les 2 lignes de trimmer

std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;
25

http://ideone.com/nFVtEo

std::string trim(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it))
        it++;

    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit))
        rit++;

    return std::string(it, rit.base());
}
19
Pushkoff

Dans le cas d'une chaîne vide, votre code suppose que l'ajout de 1 à string::npos donne 0. string::npos est du type string::size_type, qui n'est pas signé. Ainsi, vous vous appuyez sur le comportement de débordement de l'addition.

15
Greg Hewgill

Caché de Cplusplus.com

std::string choppa(const std::string &t, const std::string &ws)
{
    std::string str = t;
    size_t found;
    found = str.find_last_not_of(ws);
    if (found != std::string::npos)
        str.erase(found+1);
    else
        str.clear();            // str is all whitespace

    return str;
}

Cela fonctionne aussi pour le cas nul. :-)

14
Paul Nathan

Ma solution basée sur le répondre par @Bill the Lizard .

Notez que ces fonctions renverront la chaîne vide si la chaîne d'entrée ne contient que des espaces.

const std::string StringUtils::WHITESPACE = " \n\r\t";

std::string StringUtils::Trim(const std::string& s)
{
    return TrimRight(TrimLeft(s));
}

std::string StringUtils::TrimLeft(const std::string& s)
{
    size_t startpos = s.find_first_not_of(StringUtils::WHITESPACE);
    return (startpos == std::string::npos) ? "" : s.substr(startpos);
}

std::string StringUtils::TrimRight(const std::string& s)
{
    size_t endpos = s.find_last_not_of(StringUtils::WHITESPACE);
    return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
}
10
DavidRR

Ma réponse est une amélioration par rapport à top answer pour ce message qui supprime les caractères de contrôle ainsi que les espaces (0-32 et 127 sur la table ASCII ).

std::isgraph détermine si un caractère a une représentation graphique. Vous pouvez donc l'utiliser pour modifier la réponse d'Evan afin de supprimer tout caractère qui n'a pas de représentation graphique d'un côté ou de l'autre de la chaîne. Le résultat est une solution beaucoup plus élégante:

#include <algorithm>
#include <functional>
#include <string>

/**
 * @brief Left Trim
 *
 * Trims whitespace from the left end of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */
std::string& ltrim(std::string& s) {
  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
    std::ptr_fun<int, int>(std::isgraph)));
  return s;
}

/**
 * @brief Right Trim
 *
 * Trims whitespace from the right end of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */
std::string& rtrim(std::string& s) {
  s.erase(std::find_if(s.rbegin(), s.rend(),
    std::ptr_fun<int, int>(std::isgraph)).base(), s.end());
  return s;
}

/**
 * @brief Trim
 *
 * Trims whitespace from both ends of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */
std::string& trim(std::string& s) {
  return ltrim(rtrim(s));
}

Remarque: Vous devriez également pouvoir utiliser std::iswgraph si vous avez besoin de la prise en charge des caractères larges, mais vous devrez également éditer ce code pour activer la manipulation std::wstring, ce que je n’ai pas encore testé voir la page de référence pour std::basic_string pour explorer cette option).

9
Clay Freeman

C'est ce que j'utilise. Continuez simplement à supprimer l’espace à l’avant, puis, s’il reste quelque chose, faites de même à l’arrière.

void trim(string& s) {
    while(s.compare(0,1," ")==0)
        s.erase(s.begin()); // remove leading whitespaces
    while(s.size()>0 && s.compare(s.size()-1,1," ")==0)
        s.erase(s.end()-1); // remove trailing whitespaces
}
7
synaptik

Pour ce que cela vaut, voici une mise en œuvre soignée orientée vers la performance. C'est beaucoup plus rapide que beaucoup d'autres routines de trim que j'ai vues autour de moi. Au lieu d'utiliser iterators et std :: find, il utilise des chaînes c et des index bruts. Il optimise les cas particuliers suivants: chaîne de taille 0 (ne rien faire), chaîne sans espace à couper (ne rien faire), chaîne avec uniquement des espaces de fin à couper (il suffit de redimensionner la chaîne), chaîne entièrement blanche (tout effacer) . Et enfin, dans le pire des cas (chaîne avec des espaces de début), il fait de son mieux pour effectuer une construction de copie efficace, en effectuant une seule copie, puis en déplaçant cette copie à la place de la chaîne d'origine.

void TrimString(std::string & str)
{ 
    if(str.empty())
        return;

    const auto pStr = str.c_str();

    size_t front = 0;
    while(front < str.length() && std::isspace(int(pStr[front]))) {++front;}

    size_t back = str.length();
    while(back > front && std::isspace(int(pStr[back-1]))) {--back;}

    if(0 == front)
    {
        if(back < str.length())
        {
            str.resize(back - front);
        }
    }
    else if(back <= front)
    {
        str.clear();
    }
    else
    {
        str = std::move(std::string(str.begin()+front, str.begin()+back));
    }
}
7
mbgda
s.erase(0, s.find_first_not_of(" \n\r\t"));                                                                                               
s.erase(s.find_last_not_of(" \n\r\t")+1);   
7
freeboy1015

C++ 11 est également venu avec un module expression régulière , qui peut bien sûr être utilisé pour couper les espaces de début ou de fin.

Peut-être quelque chose comme ça:

std::string ltrim(const std::string& s)
{
    static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended};
    return std::regex_replace(s, lws, "");
}

std::string rtrim(const std::string& s)
{
    static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended};
    return std::regex_replace(s, tws, "");
}

std::string trim(const std::string& s)
{
    return ltrim(rtrim(s));
}
6

Une façon élégante de le faire peut être comme

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

Et les fonctions de support sont implémentées comme:

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

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

Et une fois que tous ces éléments sont en place, vous pouvez également écrire ceci:

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

Trim C++ 11 implémentation:

static void trim(std::string &s) {
     s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), [](char c){ return std::isspace(c); }));
     s.erase(std::find_if_not(s.rbegin(), s.rend(), [](char c){ return std::isspace(c); }).base(), s.end());
}
5
GutiMac

Je suppose que si vous commencez à demander le "meilleur moyen" de couper une chaîne, je dirais une bonne implémentation serait celle qui:

  1. N'alloue pas de chaînes temporaires
  2. A des surcharges pour le rognage sur place et le rognage de copie
  3. Peut être facilement personnalisé pour accepter différentes séquences/logiques de validation

Évidemment, il y a trop de manières différentes d'aborder cela, et cela dépend de ce dont vous avez réellement besoin. Cependant, la bibliothèque standard C a encore des fonctions très utiles dans <string.h>, comme memchr. Il y a une raison pour laquelle C est toujours considéré comme le meilleur langage pour IO - sa stdlib est une pure efficacité.

inline const char* trim_start(const char* str)
{
    while (memchr(" \t\n\r", *str, 4))  ++str;
    return str;
}
inline const char* trim_end(const char* end)
{
    while (memchr(" \t\n\r", end[-1], 4)) --end;
    return end;
}
inline std::string trim(const char* buffer, int len) // trim a buffer (input?)
{
    return std::string(trim_start(buffer), trim_end(buffer + len));
}
inline void trim_inplace(std::string& str)
{
    str.assign(trim_start(str.c_str()),
        trim_end(str.c_str() + str.length()));
}

int main()
{
    char str [] = "\t \nhello\r \t \n";

    string trimmed = trim(str, strlen(str));
    cout << "'" << trimmed << "'" << endl;

    system("pause");
    return 0;
}
4
Jorma Rebane

Contribuer ma solution au bruit. trim crée par défaut une nouvelle chaîne et renvoie la chaîne modifiée pendant que trim_in_place modifie la chaîne. La fonction trim prend en charge la sémantique de déplacement de c ++ 11.

#include <string>

// modifies input string, returns input

std::string& trim_left_in_place(std::string& str) {
    size_t i = 0;
    while(i < str.size() && isspace(str[i])) { ++i; };
    return str.erase(0, i);
}

std::string& trim_right_in_place(std::string& str) {
    size_t i = str.size();
    while(i > 0 && isspace(str[i - 1])) { --i; };
    return str.erase(i, str.size());
}

std::string& trim_in_place(std::string& str) {
    return trim_left_in_place(trim_right_in_place(str));
}

// returns newly created strings

std::string trim_right(std::string str) {
    return trim_right_in_place(str);
}

std::string trim_left(std::string str) {
    return trim_left_in_place(str);
}

std::string trim(std::string str) {
    return trim_left_in_place(trim_right_in_place(str));
}

#include <cassert>

int main() {

    std::string s1(" \t\r\n  ");
    std::string s2("  \r\nc");
    std::string s3("c \t");
    std::string s4("  \rc ");

    assert(trim(s1) == "");
    assert(trim(s2) == "c");
    assert(trim(s3) == "c");
    assert(trim(s4) == "c");

    assert(s1 == " \t\r\n  ");
    assert(s2 == "  \r\nc");
    assert(s3 == "c \t");
    assert(s4 == "  \rc ");

    assert(trim_in_place(s1) == "");
    assert(trim_in_place(s2) == "c");
    assert(trim_in_place(s3) == "c");
    assert(trim_in_place(s4) == "c");

    assert(s1 == "");
    assert(s2 == "c");
    assert(s3 == "c");
    assert(s4 == "c");  
}
3
vmrob

Je ne sais pas si votre environnement est le même, mais dans le mien, la casse de chaîne vide entraînera l'abandon du programme. Je voudrais soit envelopper cet appel d'effacement avec un if (! S.empty ()), soit utiliser Boost comme déjà mentionné. 

3
Steve

Voici ce que je suis venu avec: 

std::stringstream trimmer;
trimmer << str;
trimmer >> str;

L'extraction de flux élimine automatiquement les espaces, cela fonctionne donc comme un charme.
Assez propre et élégant aussi, si je le dis moi-même. ;)

3
tzaman

Cela peut être fait plus simplement en C++ 11 grâce à l'ajout de back() et pop_back() .

while ( !s.empty() && isspace(s.back()) ) s.pop_back();
3
nobar

Voici ma version:

size_t beg = s.find_first_not_of(" \r\n");
return (beg == string::npos) ? "" : in.substr(beg, s.find_last_not_of(" \r\n") - beg);
3
nulleight

Les méthodes ci-dessus sont excellentes, mais vous souhaitez parfois utiliser une combinaison de fonctions pour ce que votre routine considère comme des espaces. Dans ce cas, l’utilisation de foncteurs pour combiner des opérations peut devenir désordonnée, je préfère donc une boucle simple que je peux modifier pour le découpage. Voici une fonction de découpage légèrement modifiée copiée à partir de la version C ici sur SO. Dans cet exemple, je coupe les caractères non alphanumériques.

string trim(char const *str)
{
  // Trim leading non-letters
  while(!isalnum(*str)) str++;

  // Trim trailing non-letters
  end = str + strlen(str) - 1;
  while(end > str && !isalnum(*end)) end--;

  return string(str, end+1);
}
2
Corwin Joy

Cette version supprime les espaces internes et les caractères non alphanumériques:

static inline std::string &trimAll(std::string &s)
{   
    if(s.size() == 0)
    {
        return s;
    }

    int val = 0;
    for (int cur = 0; cur < s.size(); cur++)
    {
        if(s[cur] != ' ' && std::isalnum(s[cur]))
        {
            s[val] = s[cur];
            val++;
        }
    }
    s.resize(val);
    return s;
}
1
Brian

Encore une autre option - supprime un ou plusieurs caractères des deux côtés.

string strip(const string& s, const string& chars=" ") {
    size_t begin = 0;
    size_t end = s.size()-1;
    for(; begin < s.size(); begin++)
        if(chars.find_first_of(s[begin]) == string::npos)
            break;
    for(; end > begin; end--)
        if(chars.find_first_of(s[end]) == string::npos)
            break;
    return s.substr(begin, end-begin+1);
}
1
Brian W.

Voici une solution facile à comprendre pour les débutants qui n’ont pas l'habitude d'écrire std:: partout et qui ne connaissent pas encore const- exactitude, iterators, STL algorithms, etc ...

#include <string>
#include <cctype> // for isspace
using namespace std;


// Left trim the given string ("  hello!  " --> "hello!  ")
string left_trim(string str) {
    int numStartSpaces = 0;
    for (int i = 0; i < str.length(); i++) {
        if (!isspace(str[i])) break;
        numStartSpaces++;
    }
    return str.substr(numStartSpaces);
}

// Right trim the given string ("  hello!  " --> "  hello!")
string right_trim(string str) {
    int numEndSpaces = 0;
    for (int i = str.length() - 1; i >= 0; i--) {
        if (!isspace(str[i])) break;
        numEndSpaces++;
    }
    return str.substr(0, str.length() - numEndSpaces);
}

// Left and right trim the given string ("  hello!  " --> "hello!")
string trim(string str) {
    return right_trim(left_trim(str));
}

J'espère que ça aide...

1
cute_ptr

Comme je voulais mettre à jour mon ancienne fonction de découpage C++ avec une approche C++ 11, j'ai testé de nombreuses réponses postées à la question. Ma conclusion est que je conserve mon ancienne solution C++! 

C’est le plus rapide au monde, même ajouter plus de caractères à vérifier (par exemple,\r\n je ne vois aucun cas d’utilisation pour\f\v) est toujours plus rapide que les solutions utilisant un algorithme.

     std::string & trimMe (std::string & str)
     {
        // right trim
        while (str.length () > 0 && (str [str.length ()-1] == ' ' || str [str.length ()-1] == '\t'))
           str.erase (str.length ()-1, 1);

        // left trim
        while (str.length () > 0 && (str [0] == ' ' || str [0] == '\t'))
           str.erase (0, 1);
        return str;
     }
1
elxala

Et ça...?

#include <iostream>
#include <string>
#include <regex>

std::string ltrim( std::string str ) {
    return std::regex_replace( str, std::regex("^\\s+"), std::string("") );
}

std::string rtrim( std::string str ) {
    return std::regex_replace( str, std::regex("\\s+$"), std::string("") );
}

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

int main() {

    std::string str = "   \t  this is a test string  \n   ";
    std::cout << "-" << trim( str ) << "-\n";
    return 0;

}

Note: Je suis encore relativement nouveau en C++, alors pardonnez-moi si je me trompe de base.

1
Duncan

Voici une implémentation simple. Pour une opération aussi simple, vous ne devriez probablement pas utiliser de constructions spéciales. La fonction isspace () intégrée prend en charge diverses formes de caractères blancs. Nous devrions donc en tirer parti. Vous devez également tenir compte des cas spéciaux où la chaîne est vide ou simplement un tas d'espaces. Couper à gauche ou à droite peut être dérivé du code suivant.

string trimSpace(const string &str) {
   if (str.empty()) return str;
   string::size_type i,j;
   i=0;
   while (i<str.size() && isspace(str[i])) ++i;
   if (i == str.size())
      return string(); // empty string
   j = str.size() - 1;
   //while (j>0 && isspace(str[j])) --j; // the j>0 check is not needed
   while (isspace(str[j])) --j
   return str.substr(i, j-i+1);
}
1
Kemin Zhou

Avec C++ 17, vous pouvez utiliser basic_string_view :: remove_prefix et basic_string_view :: remove_suffix :

std::string_view trim(std::string_view s) const
{
    s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
    s.remove_suffix((s.size() - 1) - std::min(s.find_last_not_of(" \t\r\v\n"), s.size() - 1));

    return s;
}
0
Phidelux

Je sais que la question est très ancienne, mais j’ai ajouté quelques lignes de code à la vôtre et elle réduit les espaces des deux côtés.

void trim(std::string &line){

    auto val = line.find_last_not_of(" \n\r\t") + 1;

    if(val == line.size() || val == std::string::npos){
        val = line.find_first_not_of(" \n\r\t");
        line = line.substr(val);
    }
    else
        line.erase(val);
}
0
user3229557

c ++ 11:

int i{};
string s = " h e ll \t\n  o";
string trim = " \n\t";

while ((i = s.find_first_of(trim)) != -1)
    s.erase(i,1);

cout << s;

sortie:

hello

fonctionne bien aussi avec des chaînes vides

0
user1438233

Ci-dessous, une solution (peut être deux passes). Il parcourt deux fois la partie espaces de la chaîne et une fois les espaces non blancs.

void trim(std::string& s) {                                                                                                                                                                                                               
    if (s.empty())                                                                                                                                                                                                                        
        return;                                                                                                                                                                                                                           

    int l = 0, r = s.size()  - 1;                                                                                                                                                                                                         

    while (l < s.size() && std::isspace(s[l++])); // l points to first non-whitespace char.                                                                                                                                               
    while (r >= 0 && std::isspace(s[r--])); // r points to last non-whitespace char.                                                                                                                                                      

    if (l > r)                                                                                                                                                                                                                            
        s = "";                                                                                                                                                                                                                           
    else {                                                                                                                                                                                                                                
        l--;                                                                                                                                                                                                                              
        r++;                                                                                                                                                                                                                              
        int wi = 0;                                                                                                                                                                                                                       
        while (l <= r)                                                                                                                                                                                                                    
            s[wi++] = s[l++];                                                                                                                                                                                                             
        s.erase(wi);                                                                                                                                                                                                                      
    }                                                                                                                                                                                                                                     
    return;                                                                                                                                                                                                                               
}                                          
0
UnSat

C'est bon? (Parce que ce message a absolument besoin d'une autre réponse :)

string trimBegin(string str)
{
    string whites = "\t\r\n ";
    int i = 0;
    while (whites.find(str[i++]) != whites::npos);
    str.erase(0, i);
    return str;
}

Cas similaire pour le trimEnd, inversez simplement la polaire, les index.

0
Bondolin

J'utilise celui-ci:

void trim(string &str){
    int i=0;

    //left trim
    while (isspace(str[i])!=0)
        i++;
    str = str.substr(i,str.length()-i);

    //right trim
    i=str.length()-1;
    while (isspace(str[i])!=0)
        i--;
    str = str.substr(0,i+1);
}
0
Floella
std::string trim( std::string && str )
{
    size_t end = str.find_last_not_of( " \n\r\t" );
    if ( end != std::string::npos )
        str.resize( end + 1 );

    size_t start = str.find_first_not_of( " \n\r\t" );
    if ( start != std::string::npos )
        str = str.substr( start );

    return std::move( str );
}
0
Alexander Drichel