Suppression d'espaces de début et de fin d'une chaîne

92

Comment supprimer des espaces d'un objet chaîne en C ++.
Par exemple, comment supprimer les espaces de début et de fin de l'objet chaîne ci-dessous.

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

La classe string, pour autant que je sache, ne fournit aucune méthode pour supprimer les espaces de début et de fin.

Pour ajouter au problème, comment étendre cette mise en forme pour traiter les espaces supplémentaires entre les mots de la chaîne. Par exemple,

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

En utilisant les méthodes de chaîne mentionnées dans la solution, je peux penser à faire ces opérations en deux étapes.

  1. Supprimez les espaces de début et de fin.
  2. Utilisez find_first_of, find_last_of, find_first_not_of, find_last_not_of et substr , à plusieurs reprises aux limites des mots pour obtenir le formatage souhaité.
Ankur
la source

Réponses:

128

C'est ce qu'on appelle le rognage. Si vous pouvez utiliser Boost , je le recommande.

Sinon, utilisez find_first_not_ofpour obtenir l'index du premier caractère non blanc, puis find_last_not_ofpour obtenir l'index à partir de la fin qui n'est pas un espace blanc. Avec ceux-ci, utilisez substrpour obtenir la sous-chaîne sans espace blanc environnant.

En réponse à votre modification, je ne connais pas le terme mais je devinerais quelque chose du genre "réduire", c'est ainsi que je l'ai appelé. :) (Remarque, j'ai changé l'espace blanc en paramètre, pour plus de flexibilité)

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

Résultat:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  
GManNickG
la source
je suppose que vous vouliez dire «size_t». et vous avez un off-by-one sur la sous-chaîne, devrait être substr (beginStr, endStr - beginStr + 1);
goldPseudo
Devrait site_têtre size_t? Et je pense que là où vous avez le commentaire no whitespacesignifie que la chaîne est entièrement vide ou vide.
Fred Larson
Merci, corrigé la size_tfaute de frappe et un par un dans l'édition, mais je n'ai pas remarqué que mon commentaire était inversé, merci.
GManNickG
@GMan solution très élégante. Merci.
Ankur
Bug: essayez d'exécuter "one \ ttwo" via trim (). Le résultat est une chaîne vide. Vous devez également tester endStr par rapport à std :: string :: npos.
dlchambers
48

Suppression facile des espaces de début, de fin et supplémentaires d'une chaîne std :: string en une ligne

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

suppression uniquement des espaces de début

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

ou

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

suppression uniquement des espaces de fin

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

ou

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

supprimer uniquement les espaces supplémentaires

value = regex_replace(value, std::regex(" +"), " ");
Evgeny Karpov
la source
3
Joli. Il serait utile de fournir des informations sur ce qui se passe ici, car il est difficile de comprendre ces codes.
Marcin
Fonctionne uniquement en C ++ 11, cependant.
Martin Pecka
7
Il ne supprime pas les onglets mais cela peut être corrigé. Ce qui ne peut pas être corrigé, c'est qu'il est terriblement lent (~ 100 fois plus lent que les réponses avec substrou erase).
4LegsDrivenCat
pour l'optimisation de la vitesse, regex n'est pas la solution optimale, mais elle pourrait être améliorée en créant une instance de regex une fois
Evgeny Karpov
40

J'utilise actuellement ces fonctions:

// 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
la source
21

Algorithme de coupure de chaîne Boost

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

[...]

std::string msg = "   some text  with spaces  ";
boost::algorithm::trim(msg);
Jon-hanson
la source
9

C'est ma solution pour dépouiller les espaces de début et de fin ...

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

Le résultat est "Plamen"

Plamen Stoyanov
la source
8

Voici comment vous pouvez le faire:

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

Et une fois que tout cela est en place, vous pouvez également écrire ceci:

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

Essaye ça

jha-G
la source
7

Exemple de réduction des espaces de début et de fin suivant la suggestion de Jon-hanson d'utiliser boost (supprime uniquement les espaces de fin et en attente):

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

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

boost::algorithm::trim ( str );

Résulte en "t e s t"

Il y a aussi

  • trim_left résulte en "t e s t "
  • trim_right résulte en " t e s t"
Semjon Mössinger
la source
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);
}

Dans le code ci-dessus, la fonction isSpace () est une fonction booléenne qui indique si un caractère est un espace blanc, vous pouvez implémenter cette fonction pour refléter vos besoins, ou simplement appeler isspace () depuis "ctype.h" si vous le souhaitez .

Murphy78
la source
4

Exemple de rognage des espaces de début et de fin

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

OU

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);
Thinkal VB
la source
3
Les gens n'aimeront pas regarder dans 10 pages de code pour apprendre à couper une chaîne.
Thinkal VB
2
il est cassé si la chaîne ne contient que des espaces
DAG
3

L'utilisation de la bibliothèque standard présente de nombreux avantages, mais il faut être conscient de certains cas particuliers qui provoquent des exceptions. Par exemple, aucune des réponses n'a couvert le cas où une chaîne C ++ a des caractères Unicode. Dans ce cas, si vous utilisez la fonction isspace , une exception sera levée.

J'ai utilisé le code suivant pour couper les chaînes et certaines autres opérations qui pourraient être utiles. Les principaux avantages de ce code sont: il est vraiment rapide (plus rapide que tout code que j'ai jamais testé), il n'utilise que la bibliothèque standard, et il ne provoque jamais d'exception:

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

Cela pourrait être le plus simple de tous.

Vous pouvez utiliser string::findet string::rfindpour rechercher des espaces des deux côtés et réduire la chaîne.

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
la source
2

J'ai testé cela, tout fonctionne. Donc, cette méthode processInput demandera simplement à l'utilisateur de taper quelque chose. Elle retournera une chaîne qui n'a pas d'espaces supplémentaires en interne, ni d'espaces supplémentaires au début ou à la fin. J'espère que cela t'aides. (mettez également un tas de commentaires pour le rendre simple à comprendre).

vous pouvez voir comment l'implémenter dans le main () en bas

#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
la source
2

Pourquoi compliquer?

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

Cela fonctionne même si boost échouait, pas de regex, pas de trucs bizarres ni de bibliothèques.

EDIT: Correction du commentaire de MM.

Jack Of Blades
la source
Ceci est quelque peu inefficace, par rapport au calcul de la longueur de l'espace blanc et à l'utilisation d'un seul appel d'effacement pour chaque extrémité
MM
1

C ++ 17 introduit std::basic_string_view, un modèle de classe qui fait référence à une séquence contiguë constante d'objets de type char, c'est-à-dire une vue de la chaîne. En plus d'avoir une interface très similaire à std::basic_string, il a deux fonctions supplémentaires remove_prefix():, qui réduit la vue en faisant avancer son démarrage; et remove_suffix(), qui rétrécit la vue en déplaçant son extrémité vers l'arrière. Ceux-ci peuvent être utilisés pour réduire les espaces de début et de fin:

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

Remarque: le std::string_viewest une référence non propriétaire, il n'est donc valide que tant que la chaîne d'origine existe toujours.

Jignatius
la source
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
la source
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
la source
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
la source
0

Pour les espaces de début et de fin, que diriez-vous:

string string_trim(const string& in) {

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

}

Ou pour une phrase:

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
la source
0

Propre et net

 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());
    }
user1856722
la source
0

Non boost, non regex, juste la stringbibliothèque. C'est si 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);
}
ProjectPhysX
la source
1
... et vous avez évité d'introduire 2M de fichiers d'en-tête dans votre build!
Larry_C
0

Pour ajouter au problème, comment étendre cette mise en forme pour traiter les espaces supplémentaires entre les mots de la chaîne.

En fait, il s'agit d'un cas plus simple que de comptabiliser plusieurs espaces blancs de début et de fin. Tout ce que vous avez à faire est de supprimer les caractères d'espace blanc adjacents en double de la chaîne entière.

Le prédicat pour l'espace blanc adjacent serait simplement:

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

et vous pouvez ensuite vous débarrasser de ces caractères d'espace blanc adjacents en double avec std::unique, et l'idiome effacer-supprimer:

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

Cela laisse potentiellement un espace blanc supplémentaire à l'avant et / ou à l'arrière. Cela peut être supprimé assez facilement:

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

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

Voici une démo .

cigien
la source
-1

Ma solution pour ce problème en n'utilisant aucune méthode STL mais uniquement les méthodes propres à la chaîne C ++ est la suivante:

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

J'ai utilisé cette méthode pour passer un problème LeetCode Reverse Words in a String

Charles Wang
la source
-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
la source
-2

Qu'en est-il de l' idiome effacer-supprimer ?

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

Pardon. J'ai vu trop tard que vous ne vouliez pas supprimer tous les espaces.

Vermont.
la source
Salut, maintenant que vous savez que la réponse est fausse, vous pouvez la supprimer si vous le souhaitez. De cette façon, vous récupérerez le représentant que vous avez perdu des DV sur cette réponse :)
cigien