关于c ++:修剪std :: string的最佳方法是什么?

What's the best way to trim std::string?

我目前正在使用以下代码来修正程序中的所有std::strings

1
2
3
4
std::string s;
s.erase(s.find_last_not_of("

\t"
)+1);

它工作正常,但我想知道是否有一些可能会失败的最终案例?

当然,欢迎使用优雅的替代品和左侧解决方案的答案。


编辑从c ++ 17开始,删除了标准库的某些部分。幸运的是,从c ++ 11开始,我们有lambda这是一个很好的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include  
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

感谢https://stackoverflow.com/a/44973498/524503提出现代解决方案。

原始答案:

我倾向于使用这些中的一个来满足我的修剪需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include  
#include <functional>
#include <cctype>
#include <locale>

// trim from start
static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

// trim from end
static inline std::string &rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
}

// trim from both ends
static inline std::string &trim(std::string &s) {
    return ltrim(rtrim(s));
}

他们相当自我解释,工作得很好。

编辑:BTW,我有std::ptr_fun来帮助消除歧义std::isspace,因为实际上有第二个定义支持locales。这可能是同样的演员阵容,但我倾向于更喜欢这样。

编辑:解决有关通过引用接受参数,修改并返回参数的一些注释。我同意。我可能更喜欢的实现是两组函数,一组用于实现,另一组用于复制。一组更好的例子是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include  
#include <functional>
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

我保留上面的原始答案,但是为了保持高投票的答案仍然可用。


使用Boost的字符串算法是最简单的:

1
2
3
4
#include <boost/algorithm/string.hpp>

std::string str("hello world!");
boost::trim_right(str);

str现在是"hello world!"。还有trim_lefttrim,它们修剪两边。

如果您将_copy后缀添加到上述任何功能名称中,例如trim_copy,该函数将返回字符串的修剪副本,而不是通过引用修改它。

如果您将_if后缀添加到上述任何功能名称,例如trim_copy_if,您可以修剪满足自定义谓词的所有字符,而不仅仅是空格。


使用以下代码从std::strings(ideone)右侧修剪(尾随)空格和制表符:

1
2
3
4
5
6
7
8
9
10
11
// trim trailing spaces
size_t endpos = str.find_last_not_of(" \t");
size_t startpos = str.find_first_not_of(" \t");
if( std::string::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));
}

只是为了平衡问题,我也会包含左边的修剪代码(ideone):

1
2
3
4
5
6
// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )
{
    str = str.substr( startpos );
}


派对迟到了,但没关系。现在C ++ 11在这里,我们有lambdas和auto变量。所以我的版本,也处理所有空格和空字符串,是:

1
2
3
4
5
6
7
8
9
10
#include <cctype>
#include <string>
#include

inline std::string trim(const std::string &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));
}

我们可以从wsfront创建一个反向迭代器,并将其用作第二个find_if_not中的终止条件,但这仅适用于全空白字符串,而gcc 4.8至少不够智能推断带有auto的反向迭代器(std::string::const_reverse_iterator)的类型。我不知道构造反向迭代器有多贵,所以YMMV在这里。通过此更改,代码如下所示:

1
2
3
4
5
inline std::string trim(const std::string &s)
{
   auto  wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
   return std::string(wsfront,std::find_if_not(s.rbegin(),std::string::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base());
}


你在做什么是好的和强大的。我已经使用了相同的方法很长一段时间,我还没有找到一个更快的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const char* ws =" \t

\f\v"
;

// trim from end of string (right)
inline std::string& rtrim(std::string& s, const char* t = ws)
{
    s.erase(s.find_last_not_of(t) + 1);
    return s;
}

// trim from beginning of string (left)
inline std::string& ltrim(std::string& s, const char* t = ws)
{
    s.erase(0, s.find_first_not_of(t));
    return s;
}

// trim from both ends of string (right then left)
inline std::string& trim(std::string& s, const char* t = ws)
{
    return ltrim(rtrim(s, t), t);
}

通过提供要修剪的字符,您可以灵活地修剪非空白字符,并且只能修剪您想要修剪的字符。


试试这个,它对我有用。

1
2
3
4
5
6
inline std::string trim(std::string& str)
{
    str.erase(0, str.find_first_not_of(' '));       //prefixing spaces
    str.erase(str.find_last_not_of(' ')+1);         //surfixing spaces
    return str;
}


我喜欢tzaman的解决方案,唯一的问题是它不修剪只包含空格的字符串。

要纠正1个缺陷,请在2条修剪线之间添加str.clear()

1
2
3
4
std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;


http://ideone.com/nFVtEo

1
2
3
4
5
6
7
8
9
10
11
12
std::string trim(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it))
        it++;

    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit))
        rit++;

    return std::string(it, rit.base());
}


对于空字符串,您的代码假定向string::npos添加1给出0. string::npos的类型为string::size_type,这是无符号的。因此,您依赖于添加的溢出行为。


被Cplusplus.com黑掉了

1
2
3
4
5
6
7
8
9
10
11
12
std::string choppa(const std::string &t, const std::string &ws)
{
    std::string str = t;
    size_t found;
    found = str.find_last_not_of(ws);
    if (found != std::string::npos)
        str.erase(found+1);
    else
        str.clear();            // str is all whitespace

    return str;
}

这适用于null情况。 :-)


我的解决方案基于@Bill蜥蜴的回答。

请注意,如果输入字符串只包含空格,则这些函数将返回空字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const std::string StringUtils::WHITESPACE ="

\t"
;

std::string StringUtils::Trim(const std::string& s)
{
    return TrimRight(TrimLeft(s));
}

std::string StringUtils::TrimLeft(const std::string& s)
{
    size_t startpos = s.find_first_not_of(StringUtils::WHITESPACE);
    return (startpos == std::string::npos) ?"" : s.substr(startpos);
}

std::string StringUtils::TrimRight(const std::string& s)
{
    size_t endpos = s.find_last_not_of(StringUtils::WHITESPACE);
    return (endpos == std::string::npos) ?"" : s.substr(0, endpos+1);
}

我的答案是对这篇文章的最佳答案的改进,它修剪了控制字符和空格(ASCII表上的0-32和127)。

std::isgraph确定角色是否具有图形表示,因此您可以使用此方法更改Evan的答案,以从字符串的任一侧删除任何没有图形表示的字符。结果是一个更优雅的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include
#include <functional>
#include <string>

/**
 * @brief Left Trim
 *
 * Trims whitespace from the left end of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */

std::string& ltrim(std::string& s) {
  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
    std::ptr_fun<int, int>(std::isgraph)));
  return s;
}

/**
 * @brief Right Trim
 *
 * Trims whitespace from the right end of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */

std::string& rtrim(std::string& s) {
  s.erase(std::find_if(s.rbegin(), s.rend(),
    std::ptr_fun<int, int>(std::isgraph)).base(), s.end());
  return s;
}

/**
 * @brief Trim
 *
 * Trims whitespace from both ends of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */

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

注意:或者,如果您需要支持宽字符,则应该可以使用std::iswgraph,但是您还必须编辑此代码以启用std::wstring操作,这是我尚未测试的内容(请参阅参考页面)用于std::basic_string来探索此选项)。


这就是我使用的。只需继续从前面移除空间,然后,如果还有任何东西,请从背面做同样的事情。

1
2
3
4
5
6
void trim(string& 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
}

使用C ++ 11还有一个正则表达式模块,当然可以用来修剪前导或尾随空格。

也许是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::string ltrim(const std::string& s)
{
    static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended};
    return std::regex_replace(s, lws,"");
}

std::string rtrim(const std::string& s)
{
    static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended};
    return std::regex_replace(s, tws,"");
}

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


对于它的价值,这是一个关注性能的修剪实现。它比我见过的许多其他修剪程序要快得多。它使用原始c字符串和索引,而不是使用迭代器和std :: finds。它优化了以下特殊情况:size 0 string(什么都不做),没有要修剪的空格的字符串(什么都不做),只有尾随空格的字符串要修剪(只调整字符串大小),字符串完全是空格(只是清除字符串) 。最后,在最坏的情况下(带有前导空格的字符串),它会尽最大努力执行有效的复制构造,只执行1个副本,然后移动该副本来代替原始字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void TrimString(std::string & 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));
    }
}


1
2
3
4
5
6
s.erase(0, s.find_first_not_of("

\t"
));                                                                                              
s.erase(s.find_last_not_of("

\t"
)+1);


这样做的优雅方式可能就像

1
2
3
4
std::string & trim(std::string & str)
{
   return ltrim(rtrim(str));
}

支持功能实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
std::string & ltrim(std::string & str)
{
  auto it =  std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( str.begin() , it);
  return str;  
}

std::string & rtrim(std::string & str)
{
  auto it =  std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( it.base() , str.end() );
  return str;  
}

一旦你完成所有这些,你也可以这样写:

1
2
3
4
5
std::string trim_copy(std::string const & str)
{
   auto s = str;
   return ltrim(rtrim(s));
}

修剪C ++ 11实现:

1
2
3
4
static void trim(std::string &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());
}

使用C ++ 17,您可以使用basic_string_view :: remove_prefix和basic_string_view :: remove_suffix:

1
2
3
4
5
6
7
8
9
10
11
std::string_view trim(std::string_view s) const
{
    s.remove_prefix(std::min(s.find_first_not_of(" \t
\v
"
), s.size()));
    s.remove_suffix((s.size() - 1) - std::min(s.find_last_not_of(" \t
\v
"
), s.size() - 1));

    return s;
}

我想如果你开始要求修剪字符串的"最佳方法",我会说一个好的实现将是:

  • 不分配临时字符串
  • 具有就地装饰和复制装饰的过载
  • 可以轻松定制以接受不同的验证序列/逻辑
  • 显然,有太多不同的方法可以解决这个问题,这绝对取决于你真正需要什么。但是,C标准库在中仍然有一些非常有用的功能,比如memchr。有一个原因使C仍然被认为是IO的最佳语言 - 它的stdlib是纯粹的效率。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    inline const char* trim_start(const char* str)
    {
        while (memchr(" \t

    "
    , *str, 4))  ++str;
        return str;
    }
    inline const char* trim_end(const char* end)
    {
        while (memchr(" \t

    "
    , end[-1], 4)) --end;
        return end;
    }
    inline std::string trim(const char* buffer, int len) // trim a buffer (input?)
    {
        return std::string(trim_start(buffer), trim_end(buffer + len));
    }
    inline void trim_inplace(std::string& str)
    {
        str.assign(trim_start(str.c_str()),
            trim_end(str.c_str() + str.length()));
    }

    int main()
    {
        char str [] ="\t
    hello
     \t
    "
    ;

        string trimmed = trim(str, strlen(str));
        cout <<"'" << trimmed <<"'" << endl;

        system("pause");
        return 0;
    }

    这是我想出的:

    1
    2
    3
    std::stringstream trimmer;
    trimmer << str;
    trimmer >> str;

    流提取自动消除空白,因此这就像一个魅力。
    如果我自己这么说的话,也很干净优雅。 ;)


    为噪音贡献我的解决方案。 trim默认创建一个新字符串并返回修改后的字符串,而trim_in_place修改传递给它的字符串。 trim函数支持c ++ 11移动语义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    #include <string>

    // modifies input string, returns input

    std::string& trim_left_in_place(std::string& 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::string& trim_in_place(std::string& str) {
        return trim_left_in_place(trim_right_in_place(str));
    }

    // returns newly created strings

    std::string trim_right(std::string str) {
        return trim_right_in_place(str);
    }

    std::string trim_left(std::string str) {
        return trim_left_in_place(str);
    }

    std::string trim(std::string str) {
        return trim_left_in_place(trim_right_in_place(str));
    }

    #include <cassert>

    int main() {

        std::string s1(" \t

     "
    );
        std::string s2("  

    c"
    );
        std::string s3("c \t");
        std::string s4("  
    c"
    );

        assert(trim(s1) =="");
        assert(trim(s2) =="c");
        assert(trim(s3) =="c");
        assert(trim(s4) =="c");

        assert(s1 ==" \t

     "
    );
        assert(s2 =="  

    c"
    );
        assert(s3 =="c \t");
        assert(s4 =="  
    c"
    );

        assert(trim_in_place(s1) =="");
        assert(trim_in_place(s2) =="c");
        assert(trim_in_place(s3) =="c");
        assert(trim_in_place(s4) =="c");

        assert(s1 =="");
        assert(s2 =="c");
        assert(s3 =="c");
        assert(s4 =="c");  
    }

    由于添加了back()pop_back(),因此可以在C ++ 11中更简单地完成此操作。

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


    这是我的版本:

    1
    2
    3
    4
    5
    6
    size_t beg = s.find_first_not_of("

    "
    );
    return (beg == string::npos) ?"" : in.substr(beg, s.find_last_not_of("

    "
    ) - beg);


    我不确定你的环境是否相同,但在我的情况下,空字符串大小写会导致程序中止。我要用if(!s.empty())包装那个erase调用,或者如前所述使用Boost。


    上面的方法很棒,但有时候你想要将一些函数组合用于你的例程所认为的空格。在这种情况下,使用仿函数来组合操作会变得混乱,所以我更喜欢一个简单的循环,我可以为修剪修改。这是在SO上从C版本复制的略微修改的修剪函数。在此示例中,我正在修剪非字母数字字符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    string trim(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 string(str, end+1);
    }

    这是一个直接的实现。对于这样一个简单的操作,您可能不应该使用任何特殊构造。内置的isspace()函数负责处理各种形式的白色字符,因此我们应该利用它。您还必须考虑字符串为空或只是一堆空格的特殊情况。向左或向右修剪可以从以下代码派生。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    string trimSpace(const string &str) {
       if (str.empty()) return str;
       string::size_type i,j;
       i=0;
       while (i<str.size() && isspace(str[i])) ++i;
       if (i == str.size())
          return string(); // empty string
       j = str.size() - 1;
       //while (j>0 && isspace(str[j])) --j; // the j>0 check is not needed
       while (isspace(str[j])) --j
       return str.substr(i, j-i+1);
    }

    由于我想用C ++ 11方法更新旧的C ++ trim函数,我已经测试了很多已发布的问题答案。我的结论是我保留了旧的C ++解决方案!

    它是最快的一个,甚至添加更多的字符来检查(例如 r n我看到没有用于 f v的用例)仍然比使用算法的解决方案更快。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    std::string & trimMe (std::string & str)
    {
       // right trim
       while (str.length () > 0 && (str [str.length ()-1] == ' ' || str [str.length ()-1] == '\t'))
          str.erase (str.length ()-1, 1);

       // left trim
       while (str.length () > 0 && (str [0] == ' ' || str [0] == '\t'))
          str.erase (0, 1);
       return str;
    }

    那这个呢...?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include <iostream>
    #include <string>
    #include <regex>

    std::string ltrim( std::string str ) {
        return std::regex_replace( str, std::regex("^\\s+"), std::string("") );
    }

    std::string rtrim( std::string str ) {
        return std::regex_replace( str, std::regex("\\s+$"), std::string("") );
    }

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

    int main() {

        std::string str ="   \t  this is a test string  
      "
    ;
        std::cout <<"-" << trim( str ) <<"-
    "
    ;
        return 0;

    }

    注意:我还是比较新的C ++,所以如果我不在这里,请原谅我。


    这是一个易于理解的解决方案,对于不习惯在任何地方编写std::并且还不熟悉const -correctness,iterator s,STL algorithm等的初学者来说......

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #include <string>
    #include <cctype> // for isspace
    using namespace std;


    // Left trim the given string ("  hello! " -->"hello! ")
    string left_trim(string 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!")
    string right_trim(string 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 trim the given string ("  hello! " -->"hello!")
    string trim(string str) {
        return right_trim(left_trim(str));
    }

    希望能帮助到你...


    还有一个选择 - 从两端删除一个或多个字符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    string strip(const string& s, const string& 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]) == string::npos)
                break;
        return s.substr(begin, end-begin+1);
    }

    此版本修剪内部空白和非字母数字:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    static inline std::string &trimAll(std::string &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;
    }

    这有什么好处? (因为这篇文章完全需要另一个答案:)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    string trimBegin(string str)
    {
        string whites ="\t

    "
    ;
        int i = 0;
        while (whites.find(str[i++]) != whites::npos);
        str.erase(0, i);
        return str;
    }

    类似于trimEnd的情况,只需反转极化指标。


    我正在使用这个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void trim(string &str){
        int i=0;

        //left trim
        while (isspace(str[i])!=0)
            i++;
        str = str.substr(i,str.length()-i);

        //right trim
        i=str.length()-1;
        while (isspace(str[i])!=0)
            i--;
        str = str.substr(0,i+1);
    }


    C ++ 11:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int i{};
    string s =" h e ll \t
      o"
    ;
    string trim ="
    \t"
    ;

    while ((i = s.find_first_of(trim)) != -1)
        s.erase(i,1);

    cout << s;

    输出:

    1
    hello

    使用空字符串也可以正常工作


    我知道这是一个非常古老的问题,但我已经为你添加了几行代码,它从两端修剪了空白。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void trim(std::string &line){

        auto val = line.find_last_not_of("

    \t"
    ) + 1;

        if(val == line.size() || val == std::string::npos){
            val = line.find_first_not_of("

    \t"
    );
            line = line.substr(val);
        }
        else
            line.erase(val);
    }

    以下是一次通过(可能是两次通过)解决方案。它遍历字符串的白色空间部分两次和非空白部分一次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    void trim(std::string& s) {                                                                                                                                                                                                              
        if (s.empty())                                                                                                                                                                                                                        
            return;                                                                                                                                                                                                                          

        int l = 0, r = s.size()  - 1;                                                                                                                                                                                                        

        while (l < s.size() && std::isspace(s[l++])); // l points to first non-whitespace char.                                                                                                                                              
        while (r >= 0 && std::isspace(s[r--])); // r points to last non-whitespace char.                                                                                                                                                      

        if (l > r)                                                                                                                                                                                                                            
            s ="";                                                                                                                                                                                                                          
        else {                                                                                                                                                                                                                                
            l--;                                                                                                                                                                                                                              
            r++;                                                                                                                                                                                                                              
            int wi = 0;                                                                                                                                                                                                                      
            while (l <= r)                                                                                                                                                                                                                    
                s[wi++] = s[l++];                                                                                                                                                                                                            
            s.erase(wi);                                                                                                                                                                                                                      
        }                                                                                                                                                                                                                                    
        return;                                                                                                                                                                                                                              
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    std::string trim( std::string && str )
    {
        size_t end = str.find_last_not_of("

    \t"
    );
        if ( end != std::string::npos )
            str.resize( end + 1 );

        size_t start = str.find_first_not_of("

    \t"
    );
        if ( start != std::string::npos )
            str = str.substr( start );

        return std::move( str );
    }

    看来我参加派对的时间已经很晚了 - 我简直不敢相信7年前这个问题!

    这是我对这个问题的看法。我正在研究一个项目,我现在不想经历使用Boost的麻烦。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    std::string trim(std::string str) {
        if(str.length() == 0) return str;

        int beg = 0, end = str.length() - 1;
        while (str[beg] == ' ') {
            beg++;
        }

        while (str[end] == ' ') {
            end--;
        }

        return str.substr(beg, end - beg + 1);
    }

    此解决方案将从左侧和右侧进行修剪。


    我真烦人

    • 必须谷歌吧
    • 发现我必须使用火箭科学
    • 字符串中没有简单的trim / toupper函数

    对我来说,这是解决它的最快方法:

    1
    2
    3
    CString tmp(line.c_str());
    tmp = tmp.Trim().MakeLower();
    string buffer = tmp;

    好吧,我可以使用lambda操作,迭代器和所有东西。但我只需要处理字符串而不是字符......