Menghapus spasi di depan dan di belakangnya dari string

92

Cara menghapus spasi dari objek string di C ++.
Misalnya, cara menghapus spasi di depan dan di belakang dari objek string di bawah ini.

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

Kelas string, sejauh yang saya tahu, tidak menyediakan metode apa pun untuk menghapus spasi di depan dan di belakang.

Untuk menambah masalah, bagaimana memperluas pemformatan ini untuk memproses spasi ekstra di antara kata-kata dalam string. Sebagai contoh,

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

Dengan menggunakan metode string yang disebutkan dalam solusi, saya dapat memikirkan melakukan operasi ini dalam dua langkah.

  1. Hapus spasi di depan dan di belakang.
  2. Gunakan find_first_of, find_last_of, find_first_not_of, find_last_not_of dan substr , berulang kali pada batas kata untuk mendapatkan format yang diinginkan.
Ankur
sumber

Jawaban:

128

Ini disebut pemangkasan. Jika Anda dapat menggunakan Boost , saya akan merekomendasikannya.

Jika tidak, gunakan find_first_not_ofuntuk mendapatkan indeks dari karakter non-spasi putih pertama, lalu find_last_not_ofuntuk mendapatkan indeks dari akhir yang bukan spasi. Dengan ini, gunakan substruntuk mendapatkan sub-string tanpa spasi di sekitarnya.

Menanggapi suntingan Anda, saya tidak tahu istilahnya tetapi saya akan menebak sesuatu di sepanjang baris "mengurangi", jadi itulah yang saya menyebutnya. :) (Catatan, saya telah mengubah white-space menjadi parameter, untuk fleksibilitas)

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

Hasil:

[too much               space]  
[too much space]  
[too-much-space]  
[one  
two]  
GManNickG
sumber
saya berasumsi yang Anda maksud 'size_t'. dan Anda mendapatkan satu off-by-satu pada substring, harus substr (beginStr, endStr - beginStr + 1);
emasPseudo
Harus site_tmenjadi size_t? Dan saya pikir di mana Anda memiliki komentar no whitespaceberarti string itu semua spasi atau kosong.
Fred Larson
Terima kasih, perbaiki size_tkesalahan ketik dan off-by-one dalam pengeditan, tetapi tidak melihat komentar saya terbalik, terima kasih.
GManNickG
@GMan solusinya sangat elegant. Terima kasih.
Ankur
Bug: coba jalankan "satu \ ttwo" melalui trim (). Hasilnya adalah string kosong. Anda juga perlu menguji endStr terhadap std :: string :: npos.
dlchambers
48

Mudah menghapus spasi di depan, di belakang, dan ekstra dari string std :: dalam satu baris

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

menghapus hanya spasi di depan

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

atau

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

menghapus hanya spasi tertinggal

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

atau

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

menghapus hanya spasi ekstra

value = regex_replace(value, std::regex(" +"), " ");
Evgeny Karpov
sumber
3
Bagus. Akan berguna untuk memberikan beberapa info tentang apa yang terjadi di sini, karena sulit untuk memahami kode-kode ini.
Marcin
Hanya bekerja di C ++ 11.
Martin Pecka
7
Itu tidak menghapus tab tapi ini bisa diperbaiki. Apa yang tidak dapat diperbaiki adalah bahwa ini sangat lambat (~ 100 kali lebih lambat dari jawaban dengan substratau erase).
4LegsDrivenCat
untuk pengoptimalan kecepatan, regex bukanlah solusi optimal, tetapi dapat ditingkatkan dengan membuat regex sekali
Evgeny Karpov
40

Saya saat ini menggunakan fungsi-fungsi ini:

// 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
sumber
21

Tingkatkan algoritme pemangkasan string

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

[...]

std::string msg = "   some text  with spaces  ";
boost::algorithm::trim(msg);
jon-hanson.dll
sumber
9

Ini adalah solusi saya untuk menghilangkan ruang depan dan belakang ...

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

Hasilnya adalah "Plamen"

Plamen Stoyanov
sumber
8

Inilah cara Anda melakukannya:

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

Dan fungsi pendukung diimplementasikan sebagai:

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

Dan setelah Anda memiliki semua ini, Anda juga dapat menulis ini:

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

Coba ini

jha-G
sumber
7

Contoh untuk memangkas spasi di depan dan di belakang mengikuti saran jon-hanson untuk menggunakan dorongan (hanya menghapus spasi di belakang dan di belakang):

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

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

boost::algorithm::trim ( str );

Hasil dalam "t e s t"

Ada juga

  • trim_left menghasilkan "t e s t "
  • trim_right menghasilkan " t e s t"
Semjon Mössinger
sumber
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);
}

Pada kode di atas, fungsi isSpace () adalah fungsi boolean yang memberitahukan apakah suatu karakter adalah spasi putih, Anda dapat mengimplementasikan fungsi ini untuk mencerminkan kebutuhan Anda, atau cukup memanggil isspace () dari "ctype.h" jika Anda mau .

Murphy78
sumber
4

Contoh untuk memotong spasi di depan dan di belakang

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

ATAU

trimmedSring = aString.substr(aString.find_first_not_of(' '), (aString.find_last_not_of(' ') - aString.find_first_not_of(' ')) + 1);
Thinkal VB
sumber
3
Orang tidak akan suka melihat ke dalam 10 halaman kode untuk mempelajari cara memangkas string.
Thinkal VB
2
itu rusak jika string hanya memiliki spasi
DAG
3

Menggunakan pustaka standar memiliki banyak manfaat, tetapi orang harus menyadari beberapa kasus khusus yang menyebabkan pengecualian. Misalnya, tidak ada jawaban yang mencakup kasus di mana string C ++ memiliki beberapa karakter Unicode. Dalam kasus ini, jika Anda menggunakan fungsi isspace , pengecualian akan muncul.

Saya telah menggunakan kode berikut untuk memotong string dan beberapa operasi lain yang mungkin berguna. Manfaat utama kode ini adalah: sangat cepat (lebih cepat daripada kode apa pun yang pernah saya uji), hanya menggunakan pustaka standar, dan tidak pernah menyebabkan pengecualian:

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

Ini mungkin yang paling sederhana dari semuanya.

Anda dapat menggunakan string::finddan string::rfinduntuk menemukan spasi dari kedua sisi dan mengurangi string.

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);
    }
}
pengguna2983960
sumber
2

Saya sudah menguji ini, semuanya berhasil. Jadi metode processInput ini hanya akan meminta pengguna untuk mengetikkan sesuatu. Ini akan mengembalikan string yang tidak memiliki spasi ekstra secara internal, atau spasi ekstra di awal atau akhir. Semoga ini membantu. (juga beri banyak komentar agar mudah dipahami).

Anda bisa melihat bagaimana menerapkannya di main () di bagian bawah

#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
sumber
2

Mengapa mempersulit?

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

Ini berfungsi bahkan jika peningkatan gagal, tidak ada regex, tidak ada hal-hal aneh atau pustaka.

EDIT: Perbaiki untuk komentar MM.

Jack Of Blades
sumber
Ini agak tidak efisien, dibandingkan dengan menghitung panjang spasi dan menggunakan satu panggilan penghapusan untuk setiap ujung
MM
1

C ++ 17 diperkenalkan std::basic_string_view, template kelas yang mengacu pada urutan berdekatan konstan dari objek seperti karakter, yaitu tampilan string. Selain memiliki antarmuka yang sangat mirip std::basic_string, ia memiliki dua fungsi tambahan remove_prefix():, yang mengecilkan tampilan dengan menggerakkan awalnya; dan remove_suffix(), yang mengecilkan tampilan dengan menggerakkan ujungnya ke belakang. Ini dapat digunakan untuk memangkas ruang depan dan belakang:

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

Catatan: the std::string_viewadalah referensi non-pemilik, jadi ini hanya valid selama string aslinya masih ada.

jignatius.dll
sumber
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
sumber
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
sumber
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
sumber
0

Untuk spasi di depan dan di belakang, bagaimana dengan:

string string_trim(const string& in) {

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

}

Atau untuk kalimat:

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
sumber
0

rapi dan bersih

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

Tidak boost, tidak regex, hanya stringperpustakaan. Sesederhana itu.

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
sumber
1
... dan Anda menghindari memasukkan 2 juta file header ke dalam build Anda!
Larry_C
0

Untuk menambah masalah, bagaimana memperluas pemformatan ini untuk memproses spasi ekstra di antara kata-kata dalam string.

Sebenarnya, ini adalah kasus yang lebih sederhana daripada memperhitungkan beberapa karakter spasi kosong di depan dan di belakang. Yang perlu Anda lakukan adalah menghapus karakter spasi putih duplikat yang berdekatan dari seluruh string.

Predikat untuk white space yang berdekatan adalah:

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

lalu Anda dapat membuang duplikat karakter spasi-putih yang berdekatan itu dengan std::unique, dan idiom hapus-hapus:

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

Ini berpotensi meninggalkan karakter spasi kosong ekstra di depan dan / atau belakang. Ini dapat dihapus dengan mudah:

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

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

Berikut demo nya .

cigien.dll
sumber
-1

Solusi saya untuk masalah ini tidak menggunakan metode STL tetapi hanya metode string C ++ sendiri adalah sebagai berikut:

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

Saya telah menggunakan metode ini untuk melewati masalah LeetCode Kata Terbalik dalam String

Charles Wang
sumber
-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 Strelet
sumber
-2

Bagaimana dengan idiom hapus-hapus ?

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

Maaf. Saya terlambat melihat bahwa Anda tidak ingin menghapus semua spasi.

vt.
sumber
Hai, sekarang Anda tahu jawabannya salah, Anda dapat menghapusnya jika mau. Dengan begitu, Anda akan mendapatkan kembali reputasi yang hilang dari DV karena jawaban ini :)
cigien