Xóa khoảng trắng đầu và cuối khỏi chuỗi


92

Cách xóa khoảng trắng khỏi đối tượng chuỗi trong C ++.
Ví dụ: làm thế nào để loại bỏ khoảng trắng đầu và cuối khỏi đối tượng chuỗi bên dưới.

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

Lớp string, theo như tôi biết, không cung cấp bất kỳ phương thức nào để loại bỏ các khoảng trắng ở đầu và cuối.

Để thêm vào vấn đề, làm thế nào để mở rộng định dạng này để xử lý các khoảng trắng thừa giữa các từ của chuỗi. Ví dụ,

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

Sử dụng các phương thức chuỗi được đề cập trong giải pháp, tôi có thể nghĩ rằng thực hiện các thao tác này theo hai bước.

  1. Loại bỏ khoảng trắng đầu và cuối.
  2. Sử dụng find_first_of, find_last_of, find_first_not_of, find_last_not_of và substr , lặp đi lặp lại ở các ranh giới từ để có được định dạng mong muốn.

Câu trả lời:


128

Điều này được gọi là cắt tỉa. Nếu bạn có thể sử dụng Boost , tôi khuyên bạn nên sử dụng nó.

Nếu không, hãy sử dụng find_first_not_ofđể lấy chỉ mục của ký tự không phải khoảng trắng đầu tiên, sau đó find_last_not_ofđể lấy chỉ mục từ cuối không phải là khoảng trắng. Với những điều này, sử dụng substrđể lấy chuỗi con không có khoảng trắng xung quanh.

Để trả lời cho chỉnh sửa của bạn, tôi không biết thuật ngữ này nhưng tôi đoán một cái gì đó dọc theo dòng "giảm", vì vậy đó là những gì tôi đã gọi nó. :) (Lưu ý, tôi đã thay đổi khoảng trắng thành một tham số, để linh hoạt)

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

Kết quả:

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

tôi cho rằng ý bạn là 'size_t'. và bạn đã có một phần tử trên chuỗi con, phải là substr (beginStr, endStr - beginStr + 1);
goldPseudo

Nên site_tđược size_t? Và tôi nghĩ rằng nơi bạn có nhận xét no whitespacecó nghĩa là chuỗi là tất cả khoảng trắng hoặc trống.
Fred Larson

Cảm ơn, đã sửa size_tlỗi chính tả và lỗi từng lỗi trong bản chỉnh sửa, nhưng không nhận thấy nhận xét của tôi bị đảo ngược, cảm ơn.
GManNickG

@GMan giải pháp rất thanh lịch. Cảm ơn.
Ankur

Lỗi: hãy thử chạy "one \ ttwo" qua trim (). Kết quả là một chuỗi trống. Bạn cũng cần kiểm tra endStr đối với std :: string :: npos.
dlchambers

48

Dễ dàng loại bỏ các khoảng trống ở đầu, cuối và thừa khỏi chuỗi std :: trong một dòng

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

chỉ xóa các khoảng trắng ở đầu

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

hoặc là

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

chỉ xóa dấu cách ở cuối

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

hoặc là

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

chỉ xóa các khoảng trống thừa

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

3
Tốt lắm. Sẽ rất hữu ích nếu cung cấp một số thông tin về những gì đang xảy ra ở đây, vì rất khó để hiểu những mã này.
Marcin

Tuy nhiên, chỉ hoạt động trong C ++ 11.
Martin Pecka

7
Nó không loại bỏ các tab nhưng điều này có thể được sửa chữa. Điều không thể sửa được là nó rất chậm (chậm hơn ~ 100 lần so với câu trả lời có substrhoặc erase).
4LegsDrivenCat

cho tốc độ tối ưu hóa regex không phải là giải pháp tối ưu, nhưng nó có thể được cải thiện bằng cách tạo ra một thể hiện của regex một lần
Evgeny Karpov

40

Tôi hiện đang sử dụng các chức năng này:

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

Đây là giải pháp của tôi để loại bỏ các khoảng trống ở đầu và cuối ...

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

Kết quả là "Plamen"


8

Đây là cách bạn có thể làm điều đó:

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

Và các chức năng hỗ trợ được thực hiện như:

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

Và khi bạn đã có tất cả những điều này, bạn cũng có thể viết như sau:

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

Thử đi


7

Ví dụ về việc cắt các khoảng trắng ở đầu và cuối theo gợi ý của jon-hanson để sử dụng boost (chỉ xóa các khoảng trắng ở cuối và đang chờ xử lý):

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

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

boost::algorithm::trim ( str );

Kết quả trong "t e s t"

Ngoài ra còn có

  • trim_left kết quả trong "t e s t "
  • trim_right kết quả trong " 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);
}

Trong đoạn mã trên, hàm isSpace () là một hàm boolean cho biết một ký tự có phải là khoảng trắng hay không, bạn có thể triển khai hàm này để phản ánh nhu cầu của mình hoặc chỉ cần gọi isspace () từ "ctype.h" nếu bạn muốn .


4

Ví dụ để cắt bỏ các khoảng trống ở đầu và cuối

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

HOẶC LÀ

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

3
Mọi người sẽ không thích nhìn vào 10 trang mã để tìm hiểu cách cắt một chuỗi.
Thinkal VB

2
nó bị hỏng nếu chuỗi chỉ có khoảng trắng
DAG

3

Sử dụng thư viện chuẩn có nhiều lợi ích, nhưng phải lưu ý một số trường hợp đặc biệt gây ra ngoại lệ. Ví dụ: không có câu trả lời nào đề cập đến trường hợp chuỗi C ++ có một số ký tự Unicode. Trong trường hợp này, nếu bạn sử dụng hàm isspace , một ngoại lệ sẽ được đưa ra.

Tôi đã sử dụng đoạn mã sau để cắt bớt dây và một số thao tác khác có thể hữu ích. Những lợi ích chính của mã này là: nó thực sự nhanh (nhanh hơn bất kỳ mã nào tôi từng thử nghiệm), nó chỉ sử dụng thư viện chuẩn và không bao giờ gây ra ngoại lệ:

#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

Đây có thể là cách đơn giản nhất.

Bạn có thể sử dụng string::findstring::rfindđể tìm khoảng trắng từ cả hai phía và giảm chuỗi.

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

Tôi đã thử nghiệm điều này, tất cả đều hoạt động. Vì vậy, phương thức processInput này sẽ chỉ yêu cầu người dùng nhập một thứ gì đó vào. Nó sẽ trả về một chuỗi không có khoảng trắng thừa bên trong, cũng không có khoảng trắng thừa ở đầu hoặc cuối. Hi vọng điêu nay co ich. (cũng đặt một đống bình luận để làm cho nó đơn giản để hiểu).

bạn có thể xem cách triển khai nó trong main () ở dưới cùng

#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

Tại sao lại phức tạp?

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

Điều này hoạt động ngay cả khi boost không thành công, không có regex, không có thứ kỳ lạ hay thư viện.

EDIT: Sửa chữa cho bình luận của MM.


Điều này có phần không hiệu quả, so với việc tính toán độ dài của khoảng trắng và sử dụng một lệnh xóa duy nhất cho mỗi đầu
MM

1

C ++ 17 được giới thiệu std::basic_string_view, một mẫu lớp đề cập đến một chuỗi các đối tượng giống như char liền kề không đổi, tức là một khung nhìn của chuỗi. Ngoài việc có giao diện rất giống với std::basic_string, nó có hai chức năng bổ sung remove_prefix():, thu nhỏ chế độ xem bằng cách di chuyển bắt đầu về phía trước; và remove_suffix()thu nhỏ tầm nhìn bằng cách di chuyển đầu của nó về phía sau. Chúng có thể được sử dụng để cắt bớt khoảng trống ở đầu và cuối:

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

Lưu ý: std::string_viewtham chiếu không sở hữu, vì vậy nó chỉ hợp lệ miễn là chuỗi gốc vẫn tồn tại.


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

Đối với khoảng trắng đầu và cuối, làm thế nào về:

string string_trim(const string& in) {

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

}

Hoặc cho một câu:

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

gọn gàng và sạch sẽ

 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

Không boost, không regex, chỉ là stringthư viện. Nó đơn giản mà.

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
... và bạn đã tránh đưa 2 triệu tệp tiêu đề vào bản dựng của mình!
Larry_C

0

Để thêm vào vấn đề, làm thế nào để mở rộng định dạng này để xử lý các khoảng trắng thừa giữa các từ của chuỗi.

Trên thực tế, đây là một trường hợp đơn giản hơn so với việc tính toán nhiều ký tự khoảng trắng ở đầu và cuối. Tất cả những gì bạn cần làm là xóa các ký tự khoảng trắng liền kề trùng lặp khỏi toàn bộ chuỗi.

Vị từ cho khoảng trắng liền kề sẽ đơn giản là:

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

và sau đó bạn có thể loại bỏ các ký tự khoảng trắng liền kề trùng lặp đó với std::uniquevà thành ngữ xóa xóa:

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

Điều này có khả năng để lại một ký tự khoảng trắng thừa ở phía trước và / hoặc phía sau. Điều này có thể được loại bỏ khá dễ dàng:

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

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

Đây là một bản demo .


-1

Giải pháp của tôi cho vấn đề này không sử dụng bất kỳ phương thức STL nào mà chỉ sử dụng các phương thức riêng của chuỗi C ++ như sau :

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

Tôi đã sử dụng phương pháp này để vượt qua vấn đề LeetCode Đảo ngược các từ trong một chuỗi


-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

Còn thành ngữ xóa - xóa thì sao?

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

Lấy làm tiếc. Tôi đã thấy quá muộn rằng bạn không muốn xóa tất cả khoảng trắng.


Xin chào, bây giờ bạn biết rằng câu trả lời là sai, bạn có thể xóa nó nếu bạn muốn. Bằng cách đó, bạn sẽ nhận lại được đại diện mà bạn đã mất từ ​​DV cho câu trả lời này :)
cigien
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.