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


J'utilise actuellement le code suivant pour corriger tous les 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 y a des cas où cela pourrait échouer?

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


647
2017-10-19 19:23


origine


Réponses:


MODIFIER Depuis c ++ 17, certaines parties de la bibliothèque standard ont été supprimées. Heureusement, à partir de c ++ 11, nous avons des lambdas qui sont 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;
}

Grâce à https://stackoverflow.com/a/44973498/524503 pour avoir apporté 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.

MODIFIER: BTW, j'ai std::ptr_fun là-bas pour aider à désambiguïser std::isspace car il existe en fait une seconde définition qui prend en charge les locales. Cela aurait pu être une distribution tout de même, mais j'ai tendance à aimer ça mieux.

MODIFIER: Pour répondre à certains commentaires sur l'acceptation d'un paramètre par référence, en le modifiant et en le renvoyant. Je suis d'accord. Une implémentation que je préférerais probablement serait deux ensembles de fonctions, une pour en place et une qui fait une 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 mais pour le contexte et dans l'intérêt de garder la réponse élevée votée toujours disponible.


525
2017-10-20 05:46



En utilisant Les algorithmes de cordes de Boost serait le plus facile:

#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 _copy suffixe à l'un quelconque des noms de fonctions ci-dessus, par ex. trim_copy, la fonction retournera une copie rognée de la chaîne au lieu de la modifier via une référence.

Si vous ajoutez _if suffixe à l'un quelconque des noms de fonctions ci-dessus, par ex. trim_copy_if, vous pouvez découper tous les caractères satisfaisant votre prédicat personnalisé, par opposition aux espaces uniquement.


376
2017-10-19 19:55



Utilisez le code suivant pour redimensionner les espaces (arrière) et les caractères de tabulation à partir de std::strings (Idéone):

// 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, je vais inclure le code de trim gauche aussi (Idéone):

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

55
2017-12-07 19:45



Un peu tard à la fête, mais peu importe. Maintenant C ++ 11 est ici, nous avons des variables lambdas et auto. Donc, ma version, qui gère également les espaces vides et les chaînes vides, est:

#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 faire un itérateur inverse de wsfront et l'utiliser comme condition de terminaison dans la seconde find_if_not mais cela n'est utile que dans le cas d'une chaîne tout blanc, et gcc 4.8 au moins n'est pas assez intelligent pour déduire le type de l'itérateur inverse (std::string::const_reverse_iterator) avec auto. Je ne sais pas combien coûte la construction d'un itérateur inverse, 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());
}

49
2017-07-31 17:03



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é une 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 (left & right)
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 découper les caractères qui ne sont pas des espaces et l'efficacité de découper uniquement les caractères que vous voulez couper.


33
2017-08-19 14:16



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

30
2018-06-28 00:47



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

Pour corriger ce défaut, ajoutez un str.clear () entre les deux lignes du trimmer

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

25
2017-07-05 06:37