Rimozione di spazi iniziali e finali da una stringa


92

Come rimuovere gli spazi da un oggetto stringa in C ++.
Ad esempio, come rimuovere gli spazi iniziali e finali dall'oggetto stringa sottostante.

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

La classe string, per quanto ne so, non fornisce alcun metodo per rimuovere gli spazi iniziali e finali.

Per aggiungere al problema, come estendere questa formattazione per elaborare gli spazi aggiuntivi tra le parole della stringa. Per esempio,

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

Utilizzando i metodi delle stringhe menzionati nella soluzione, posso pensare di eseguire queste operazioni in due passaggi.

  1. Rimuovi gli spazi iniziali e finali.
  2. Usa find_first_of, find_last_of, find_first_not_of, find_last_not_of e substr , ripetutamente ai confini delle parole per ottenere la formattazione desiderata.

Risposte:


128

Questo si chiama taglio. Se puoi usare Boost , lo consiglierei.

Altrimenti, utilizzare find_first_not_ofper ottenere l'indice del primo carattere diverso da uno spazio, quindi find_last_not_ofottenere l'indice dalla fine che non è uno spazio vuoto. Con questi, utilizzare substrper ottenere la sottostringa senza spazi bianchi circostanti.

In risposta alla tua modifica, non conosco il termine, ma immagino qualcosa sulla falsariga di "ridurre", quindi è così che l'ho chiamato. :) (Nota, ho cambiato lo spazio bianco per essere un parametro, per flessibilità)

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

Risultato:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  

presumo che tu intendessi "size_t". e hai uno off-by-one sulla sottostringa, dovrebbe essere substr (beginStr, endStr - beginStr + 1);
goldPseudo

Dovrebbe site_tessere size_t? E penso che dove hai il commento no whitespacesignifichi che la stringa è tutta spazi bianchi o vuota.
Fred Larson

Grazie, corretto l' size_terrore di battitura e off-by-one nella modifica, ma non ho notato che il mio commento era invertito, grazie.
GManNickG

@ GMan soluzione molto elegante. Grazie.
Ankur

Bug: prova a eseguire "one \ ttwo" tramite trim (). Il risultato è una stringa vuota. Devi anche testare endStr su std :: string :: npos.
dlchambers

48

Facile rimozione di spazi iniziali, finali ed extra da uno std :: string in una riga

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

rimuovendo solo gli spazi iniziali

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

o

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

rimuovendo solo gli spazi finali

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

o

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

rimuovendo solo spazi extra

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

3
Ben fatto. Sarebbe utile fornire alcune informazioni su ciò che sta accadendo qui, poiché è difficile comprendere questi codici.
Marcin

Funziona solo in C ++ 11, però.
Martin Pecka

7
Non rimuove le schede ma questo può essere risolto. Ciò che non può essere risolto è che è terribilmente lento (~ 100 volte più lento delle risposte con substro erase).
4LegsDrivenCat

per l'ottimizzazione della velocità la regex non è la soluzione ottimale, ma potrebbe essere migliorata creando un'istanza di regex una volta
Evgeny Karpov

40

Attualmente sto utilizzando queste funzioni:

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


9

Questa è la mia soluzione per eliminare gli spazi iniziali e finali ...

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

Il risultato è "Plamen"


8

Ecco come puoi farlo:

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

E le funzioni di supporto sono implementate come:

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

E una volta che hai tutto questo a posto, puoi scrivere anche questo:

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

Prova questo


7

Esempio per tagliare gli spazi iniziali e finali seguendo il suggerimento di jon-hanson di utilizzare boost (rimuove solo gli spazi finali e in sospeso):

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

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

boost::algorithm::trim ( str );

Risultati in "t e s t"

C'è anche

  • trim_left risultati in "t e s t "
  • trim_right risultati in " t e s t"

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

Nel codice sopra, la funzione isSpace () è una funzione booleana che dice se un carattere è uno spazio bianco, puoi implementare questa funzione per riflettere le tue esigenze, o semplicemente chiamare isspace () da "ctype.h" se vuoi .


4

Esempio per il taglio di spazi iniziali e finali

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

O

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);

3
Alla gente non piacerà esaminare 10 pagine di codice per imparare a tagliare una stringa.
Thinkal VB

2
è interrotto se la stringa contiene solo spazi
DAG

3

L'uso della libreria standard ha molti vantaggi, ma è necessario essere consapevoli di alcuni casi speciali che causano eccezioni. Ad esempio, nessuna delle risposte ha coperto il caso in cui una stringa C ++ ha alcuni caratteri Unicode. In questo caso, se usi la funzione isspace , verrà generata un'eccezione.

Ho usato il seguente codice per tagliare le stringhe e alcune altre operazioni che potrebbero tornare utili. I principali vantaggi di questo codice sono: è molto veloce (più veloce di qualsiasi codice che abbia mai testato), utilizza solo la libreria standard e non causa mai un'eccezione:

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

3

Questo potrebbe essere il più semplice di tutti.

Puoi usare string::finde string::rfindper trovare gli spazi da entrambi i lati e ridurre la stringa.

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

2

L'ho provato, funziona tutto. Quindi questo metodo processInput chiederà semplicemente all'utente di digitare qualcosa. Restituirà una stringa che non ha spazi extra internamente, né spazi extra all'inizio o alla fine. Spero che sia di aiuto. (inserire anche un mucchio di commenti per renderlo semplice da capire).

puoi vedere come implementarlo in main () in basso

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

2

Perché complicare?

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

Funziona anche se il boost dovesse fallire, nessuna regex, niente cose strane o librerie.

EDIT: correzione per il commento di MM.


Questo è un po 'inefficiente, rispetto al calcolo della lunghezza degli spazi bianchi e all'utilizzo di una singola chiamata di cancellazione per ciascuna estremità
MM

1

C ++ 17 introdotto std::basic_string_view, un modello di classe che fa riferimento a una sequenza contigua costante di oggetti simili a caratteri, ovvero una vista della stringa. Oltre ad avere un'interfaccia molto simile a std::basic_string, ha due funzioni aggiuntive remove_prefix():, che riduce la vista spostando il suo inizio in avanti; e remove_suffix(), che restringe la vista spostando la sua estremità all'indietro. Questi possono essere usati per tagliare lo spazio iniziale e finale:

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

Nota: std::string_viewè un riferimento non proprietario, quindi è valido solo fino a quando la stringa originale esiste ancora.


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

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

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

0

Per gli spazi iniziali e finali, che ne dici di:

string string_trim(const string& in) {

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

}

O per una frase:

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

0

ordinato e pulito

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

0

No boost, no regex, solo la stringbiblioteca. È così semplice.

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

1
... e hai evitato di inserire 2M di file di intestazione nella tua build!
Larry_C

0

Per aggiungere al problema, come estendere questa formattazione per elaborare gli spazi aggiuntivi tra le parole della stringa.

In realtà, questo è un caso più semplice rispetto alla contabilizzazione di più caratteri di spazi bianchi iniziali e finali. Tutto quello che devi fare è rimuovere i caratteri duplicati di spazi bianchi adiacenti dall'intera stringa.

Il predicato per lo spazio bianco adiacente sarebbe semplicemente:

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

e quindi puoi sbarazzarti di quei caratteri duplicati adiacenti di spazio bianco con std::uniquee l'idioma cancella-rimuovi:

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

Questo potenzialmente lascia un carattere di spazio bianco aggiuntivo nella parte anteriore e / o posteriore. Questo può essere rimosso abbastanza facilmente:

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

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

Ecco una demo .


-1

La mia soluzione per questo problema che non utilizza alcun metodo STL ma solo i metodi della stringa C ++ è la seguente :

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

Ho usato questo metodo per passare un problema di LeetCode Reverse Words in una stringa


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

-2

E l' idioma cancella-rimuovi ?

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

Scusate. Ho visto troppo tardi che non vuoi rimuovere tutti gli spazi bianchi.


Ciao, ora che sei consapevole che la risposta è sbagliata, puoi cancellarla se lo desideri. In questo modo, riceverai indietro il rappresentante che hai perso dai DV su questa risposta :)
cigien
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.