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

J’utilise actuellement le code suivant pour rogner toutes les std::ssortingngs de mes programmes:

 std::ssortingng 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 une solution de réglage à gauche sont les bienvenues.

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 sont une solution supérieure.

 #include  #include  #include  // sortingm from start (in place) static inline void lsortingm(std::ssortingng &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); } // sortingm from end (in place) static inline void rsortingm(std::ssortingng &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); } // sortingm from both ends (in place) static inline void sortingm(std::ssortingng &s) { lsortingm(s); rsortingm(s); } // sortingm from start (copying) static inline std::ssortingng lsortingm_copy(std::ssortingng s) { lsortingm(s); return s; } // sortingm from end (copying) static inline std::ssortingng rsortingm_copy(std::ssortingng s) { rsortingm(s); return s; } // sortingm from both ends (copying) static inline std::ssortingng sortingm_copy(std::ssortingng s) { sortingm(s); return s; } 

Merci à https://stackoverflow.com/a/44973498/524503 pour avoir mis au point la solution moderne.

Réponse originale:

J’ai tendance à utiliser l’un de ces 3 pour mes besoins de taille:

 #include  #include  #include  #include  // sortingm from start static inline std::ssortingng &lsortingm(std::ssortingng &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); return s; } // sortingm from end static inline std::ssortingng &rsortingm(std::ssortingng &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); return s; } // sortingm from both ends static inline std::ssortingng &sortingm(std::ssortingng &s) { return lsortingm(rsortingm(s)); } 

Ils sont assez explicites et fonctionnent très bien.

EDIT : BTW, j’ai std::ptr_fun là-dedans pour aider à désambiguïser std::isspace car il y a en fait une deuxième définition qui supporte les parameters régionaux. Cela aurait pu être un casting tout de même, mais j’ai tendance à aimer cela mieux.

EDIT : 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 la place et une pour la copie. Un meilleur ensemble d’exemples serait:

 #include  #include  #include  #include  // sortingm from start (in place) static inline void lsortingm(std::ssortingng &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); } // sortingm from end (in place) static inline void rsortingm(std::ssortingng &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); } // sortingm from both ends (in place) static inline void sortingm(std::ssortingng &s) { lsortingm(s); rsortingm(s); } // sortingm from start (copying) static inline std::ssortingng lsortingm_copy(std::ssortingng s) { lsortingm(s); return s; } // sortingm from end (copying) static inline std::ssortingng rsortingm_copy(std::ssortingng s) { rsortingm(s); return s; } // sortingm from both ends (copying) static inline std::ssortingng sortingm_copy(std::ssortingng s) { sortingm(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 à haute voix encore disponible.

Utiliser les algorithmes de chaîne de Boost serait plus facile:

 #include  std::ssortingng str("hello world! "); boost::sortingm_right(str); 

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


Si vous ajoutez le suffixe _copy à l’un des noms de fonction ci-dessus, par exemple sortingm_copy , la fonction retournera une copie sortingm_copy 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, sortingm_copy_if , vous pouvez couper tous les caractères satisfaisant votre prédicat personnalisé, par opposition aux espaces blancs.

Utilisez le code suivant pour corriger les espaces et les tabulations de std::ssortingngs ( ideone ):

 // sortingm trailing spaces size_t endpos = str.find_last_not_of(" \t"); size_t startpos = str.find_first_not_of(" \t"); if( std::ssortingng::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 également inclure le code de finition gauche ( ideone ):

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

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

 #include  #include  #include  inline std::ssortingng sortingm(const std::ssortingng &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 iterator inversé à 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 tout-blanc et au moins gcc 4.8 n'est pas assez intelligent pour en déduire le type iterator ( std::ssortingng::const_reverse_iterator ) avec auto . Je ne sais pas combien coûte la construction d'un iterator inversé, alors YMMV ici. Avec cette modification, le code ressemble à ceci:

 inline std::ssortingng sortingm(const std::ssortingng &s) { auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);}); return std::ssortingng(wsfront,std::find_if_not(s.rbegin(),std::ssortingng::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base()); } 

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"; // sortingm from end of ssortingng (right) inline std::ssortingng& rsortingm(std::ssortingng& s, const char* t = ws) { s.erase(s.find_last_not_of(t) + 1); return s; } // sortingm from beginning of ssortingng (left) inline std::ssortingng& lsortingm(std::ssortingng& s, const char* t = ws) { s.erase(0, s.find_first_not_of(t)); return s; } // sortingm from both ends of ssortingng (left & right) inline std::ssortingng& sortingm(std::ssortingng& s, const char* t = ws) { return lsortingm(rsortingm(s, t), t); } 

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

Essayez ceci, ça marche pour moi.

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

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 ce défaut, ajoutez un str.clear () entre les deux lignes

 std::ssortingngstream sortingmmer; sortingmmer << str; str.clear(); trimmer >> str; 

http://ideone.com/nFVtEo

 std::ssortingng sortingm(const std::ssortingng &s) { std::ssortingng::const_iterator it = s.begin(); while (it != s.end() && isspace(*it)) it++; std::ssortingng::const_reverse_iterator rit = s.rbegin(); while (rit.base() != it && isspace(*rit)) rit++; return std::ssortingng(it, rit.base()); } 

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

Piraté de Cplusplus.com

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

Cela fonctionne également pour le cas nul. 🙂

Ma solution basée sur la réponse de @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::ssortingng SsortingngUtils::WHITESPACE = " \n\r\t"; std::ssortingng SsortingngUtils::Trim(const std::ssortingng& s) { return TrimRight(TrimLeft(s)); } std::ssortingng SsortingngUtils::TrimLeft(const std::ssortingng& s) { size_t startpos = s.find_first_not_of(SsortingngUtils::WHITESPACE); return (startpos == std::ssortingng::npos) ? "" : s.substr(startpos); } std::ssortingng SsortingngUtils::TrimRight(const std::ssortingng& s) { size_t endpos = s.find_last_not_of(SsortingngUtils::WHITESPACE); return (endpos == std::ssortingng::npos) ? "" : s.substr(0, endpos+1); } 

Ma réponse est une amélioration par rapport à la première réponse pour ce post qui coupe les caractères de contrôle ainsi que les espaces (0-32 et 127 sur le tableau ASCII ).

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

 #include  #include  #include  /** * @brief Left Trim * * Trims whitespace from the left end of the provided std::ssortingng * * @param[out] s The std::ssortingng to sortingm * * @return The modified std::ssortingng& */ std::ssortingng& lsortingm(std::ssortingng& s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::ptr_fun(std::isgraph))); return s; } /** * @brief Right Trim * * Trims whitespace from the right end of the provided std::ssortingng * * @param[out] s The std::ssortingng to sortingm * * @return The modified std::ssortingng& */ std::ssortingng& rsortingm(std::ssortingng& s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::ptr_fun(std::isgraph)).base(), s.end()); return s; } /** * @brief Trim * * Trims whitespace from both ends of the provided std::ssortingng * * @param[out] s The std::ssortingng to sortingm * * @return The modified std::ssortingng& */ std::ssortingng& sortingm(std::ssortingng& s) { return lsortingm(rsortingm(s)); } 

Note: Sinon, vous devriez pouvoir utiliser std::iswgraph si vous avez besoin de support pour les caractères larges, mais vous devrez également modifier ce code pour activer la manipulation std::wssortingng , ce que je n’ai pas testé (voir le page de référence pour std::basic_ssortingng pour explorer cette option).

C’est ce que j’utilise. Continuez à enlever de l’espace de l’avant, puis, s’il rest quelque chose, faites la même chose de l’arrière.

 void sortingm(ssortingng& 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 } 

Pour ce que cela vaut, voici une mise en œuvre soignée en vue de la performance. C’est beaucoup plus rapide que beaucoup d’autres routines de sortingm que j’ai vues. Au lieu d’utiliser des iterators et std :: find, il utilise des chaînes et des index bruts. Il optimise les cas spéciaux suivants: taille 0 chaîne (ne rien faire), chaîne sans espace à couper (ne rien faire), chaîne avec uniquement des espaces blancs à la fin (il suffit de redimensionner la chaîne), chaîne entièrement blanche (efface la chaîne) . Et enfin, dans le pire des cas (chaîne avec espaces blancs en tête), il fait de son mieux pour effectuer une construction de copie efficace, en exécutant une seule copie et en déplaçant cette copie à la place de la chaîne d’origine.

 void TrimSsortingng(std::ssortingng & 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)); } } 

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

Peut-être quelque chose comme ça:

 std::ssortingng lsortingm(const std::ssortingng& s) { static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended}; return std::regex_replace(s, lws, ""); } std::ssortingng rsortingm(const std::ssortingng& s) { static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended}; return std::regex_replace(s, tws, ""); } std::ssortingng sortingm(const std::ssortingng& s) { return lsortingm(rsortingm(s)); } 

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

 std::ssortingng & sortingm(std::ssortingng & str) { return lsortingm(rsortingm(str)); } 

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

 std::ssortingng & lsortingm(std::ssortingng & str) { auto it = std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace(ch , std::locale::classic() ) ; } ); str.erase( str.begin() , it); return str; } std::ssortingng & rsortingm(std::ssortingng & str) { auto it = std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace(ch , std::locale::classic() ) ; } ); str.erase( it.base() , str.end() ); return str; } 

Et une fois que vous avez tout cela en place, vous pouvez aussi écrire ceci:

 std::ssortingng sortingm_copy(std::ssortingng const & str) { auto s = str; return lsortingm(rsortingm(s)); } 
 s.erase(0, s.find_first_not_of(" \n\r\t")); s.erase(s.find_last_not_of(" \n\r\t")+1); 

Voici ce que j’ai imaginé:

 std::ssortingngstream sortingmmer; sortingmmer << str; trimmer >> str; 

L’extraction de stream élimine automatiquement les espaces, ce qui fait que cela fonctionne comme un charme.
Assez propre et élégant aussi, si je le dis moi-même. 😉

Je suppose que si vous commencez à demander la “meilleure façon” de couper une chaîne, je dirais qu’une bonne implémentation serait celle qui:

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

Évidemment, il y a trop de façons différentes d’aborder cela et cela dépend de ce dont vous avez réellement besoin. Cependant, la bibliothèque standard C dispose encore de fonctions très utiles dans , comme memchr. Il y a une raison pour laquelle C est toujours considéré comme le meilleur langage pour IO – son stdlib est une efficacité pure.

 inline const char* sortingm_start(const char* str) { while (memchr(" \t\n\r", *str, 4)) ++str; return str; } inline const char* sortingm_end(const char* end) { while (memchr(" \t\n\r", end[-1], 4)) --end; return end; } inline std::ssortingng sortingm(const char* buffer, int len) // sortingm a buffer (input?) { return std::ssortingng(sortingm_start(buffer), sortingm_end(buffer + len)); } inline void sortingm_inplace(std::ssortingng& str) { str.assign(sortingm_start(str.c_str()), sortingm_end(str.c_str() + str.length())); } int main() { char str [] = "\t \nhello\r \t \n"; ssortingng sortingmmed = sortingm(str, strlen(str)); cout << "'" << trimmed << "'" << endl; system("pause"); return 0; } 

Trim C ++ 11 implémentation:

 static void sortingm(std::ssortingng &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()); } 

Je ne suis pas sûr que votre environnement soit le même, mais dans la mienne, la casse vide provoquera l’abandon du programme. Je voudrais soit envelopper cet appel avec un if (! S.empty ()) ou utiliser Boost comme déjà mentionné.

Cela peut être fait plus simplement en C ++ 11 en raison de l’ajout de back() et pop_back() .

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

Voici ma version:

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

Consortingbuer ma solution au bruit. sortingm défaut pour créer une nouvelle chaîne et renvoyer celle modifiée alors que sortingm_in_place modifie la chaîne qui lui est transmise. La fonction sortingm prend en charge la sémantique de déplacement c ++ 11.

 #include  // modifies input ssortingng, returns input std::ssortingng& sortingm_left_in_place(std::ssortingng& 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::ssortingng& sortingm_in_place(std::ssortingng& str) { return sortingm_left_in_place(sortingm_right_in_place(str)); } // returns newly created ssortingngs std::ssortingng sortingm_right(std::ssortingng str) { return sortingm_right_in_place(str); } std::ssortingng sortingm_left(std::ssortingng str) { return sortingm_left_in_place(str); } std::ssortingng sortingm(std::ssortingng str) { return sortingm_left_in_place(sortingm_right_in_place(str)); } #include  int main() { std::ssortingng s1(" \t\r\n "); std::ssortingng s2(" \r\nc"); std::ssortingng s3("c \t"); std::ssortingng s4(" \rc "); assert(sortingm(s1) == ""); assert(sortingm(s2) == "c"); assert(sortingm(s3) == "c"); assert(sortingm(s4) == "c"); assert(s1 == " \t\r\n "); assert(s2 == " \r\nc"); assert(s3 == "c \t"); assert(s4 == " \rc "); assert(sortingm_in_place(s1) == ""); assert(sortingm_in_place(s2) == "c"); assert(sortingm_in_place(s3) == "c"); assert(sortingm_in_place(s4) == "c"); assert(s1 == ""); assert(s2 == "c"); assert(s3 == "c"); assert(s4 == "c"); } 

c ++ 11:

 int i{}; ssortingng s = " he ll \t\no"; ssortingng sortingm = " \n\t"; while ((i = s.find_first_of(sortingm)) != -1) s.erase(i,1); cout << s; 

sortie:

 hello 

fonctionne bien aussi avec des chaînes vides

Voici une solution facile à comprendre pour les débutants peu habitués à écrire std:: partout et pas encore familier avec const -correction, les iterator s, les algorithm STL, etc …

 #include  #include  // for isspace using namespace std; // Left sortingm the given ssortingng (" hello! " --> "hello! ") ssortingng left_sortingm(ssortingng 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!") ssortingng right_sortingm(ssortingng 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 sortingm the given ssortingng (" hello! " --> "hello!") ssortingng sortingm(ssortingng str) { return right_sortingm(left_sortingm(str)); } 

J’espère que cela aide…

Les méthodes ci-dessus sont excellentes, mais parfois vous voulez utiliser une combinaison de fonctions pour ce que votre routine considère comme des espaces. Dans ce cas, utiliser des foncteurs pour combiner les opérations peut être compliqué, alors je préfère une simple boucle que je peux modifier pour la finition. Voici une fonction de sortingm légèrement modifiée, copiée depuis la version C ici sur SO. Dans cet exemple, je coupe les caractères non alphanumériques.

 ssortingng sortingm(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 ssortingng(str, end+1); } 

Cette version limite les espaces blancs et les caractères non alphanumériques:

 static inline std::ssortingng &sortingmAll(std::ssortingng &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; } 

Encore une autre option – supprime un ou plusieurs caractères des deux extrémités.

 ssortingng ssortingp(const ssortingng& s, const ssortingng& 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]) == ssortingng::npos) break; return s.substr(begin, end-begin+1); } 

Et ça…?

 #include  #include  #include  std::ssortingng lsortingm( std::ssortingng str ) { return std::regex_replace( str, std::regex("^\\s+"), std::ssortingng("") ); } std::ssortingng rsortingm( std::ssortingng str ) { return std::regex_replace( str, std::regex("\\s+$"), std::ssortingng("") ); } std::ssortingng sortingm( std::ssortingng str ) { return lsortingm( rsortingm( str ) ); } int main() { std::ssortingng str = " \t this is a test ssortingng \n "; std::cout << "-" << trim( str ) << "-\n"; return 0; } 

Note: je suis encore relativement nouveau en C ++, alors pardonnez-moi si je suis hors de la base ici.