如何修剪 std::string?

Posted

技术标签:

【中文标题】如何修剪 std::string?【英文标题】:How to trim a std::string? 【发布时间】:2010-09-18 00:36:45 【问题描述】:

我目前正在使用以下代码对我的程序中的所有 std::strings 进行右修剪:

std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);

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

当然,欢迎提供优雅替代方案和左修剪解决方案的答案。

【问题讨论】:

【参考方案1】:

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

#include <algorithm> 
#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(), [](unsigned char 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(), [](unsigned char 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://***.com/a/44973498/524503 提出现代解决方案。

原答案:

我倾向于使用以下三种中的一种来满足我的修剪需求:

#include <algorithm> 
#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));

它们是不言自明的并且工作得很好。

编辑:顺便说一句,我有std::ptr_fun 来帮助消除std::isspace 的歧义,因为实际上还有第二个定义支持语言环境。这可能是一个相同的演员,但我更喜欢这个。

编辑:解决一些关于通过引用接受参数、修改和返回参数的问题。我同意。我可能更喜欢的实现是两组函数,一组用于就地,另一组用于制作副本。一组更好的例子是:

#include <algorithm> 
#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;

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

【讨论】:

此代码在某些国际字符串上失败(在我的例子中是 shift-jis,存储在 std::string 中);我最终使用boost::trim 解决了这个问题。 我会使用指针而不是引用,以便从调用点更容易理解这些函数就地编辑字符串,而不是创建副本。 请注意,使用 isspace 您可以很容易地使用非 ASCII 字符获得未定义的行为stacked-crooked.com/view?id=49bf8b0759f0dd36dffdad47663ac69f 为什么是静态的?这是首选匿名命名空间的地方吗? @TrevorHickey,当然,如果您愿意,可以使用匿名命名空间。【参考方案2】:

使用Boost's string algorithms 是最简单的:

#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,您可以修剪所有满足您的自定义谓词的字符,而不仅仅是空格。

【讨论】:

boost 用什么来判断一个字符是否为空格? 这取决于语言环境。我的默认语言环境(VS2005,en)意味着制表符、空格、回车符、换行符、垂直制表符和换页符被修剪。 我已经使用了大量的提升,#include &lt;boost/format.hpp&gt; #include &lt;boost/tokenizer.hpp&gt; #include &lt;boost/lexical_cast.hpp&gt;,但是当已经有基于std::string::erase 的替代方案时,我担心添加&lt;boost/algorithm/string.hpp&gt; 会导致代码膨胀。很高兴在比较添加之前和之后的 MinSizeRel 构建时报告,boost 的修剪根本没有增加我的代码大小(一定已经在某个地方为此付出了代价),而且我的代码没有杂乱无章的更多功能。 @MattyT:你在这个列表中使用了什么参考(确定一个字符是否是空格)? 并没有真正回答要求 std::string 的问题(不适用于 boost 或任何其他库......)【参考方案3】:

您正在做的事情很好而且很强大。同样的方法我用了很久了,还没有找到更快的方法:

const char* ws = " \t\n\r\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);

通过提供要修剪的字符,您可以灵活地修剪非空白字符并高效地仅修剪您想要修剪的字符。

【讨论】:

如果您在 CharT 上使用 basic_string 和模板,您可以对所有字符串执行此操作,只需为空格使用模板变量,以便您像 ws 一样使用它。从技术上讲,你可以让它为 c++20 做好准备,并将它标记为 constexpr,因为这意味着内联 @Beached 确实。不过,在这里给出答案有点复杂。我已经为此编写了模板函数,它肯定是相当复杂的。我尝试了很多不同的方法,但仍然不确定哪种方法最好。【参考方案4】:

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

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

// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )

    str = str.substr( startpos );

【讨论】:

这不会检测到其他形式的空格...尤其是换行符、换行符、回车符。 对。您必须针对要修剪的空白对其进行自定义。我的特定应用程序只需要空格和制表符,但您可以添加 \n\r 来捕捉其他应用程序。 str.substr(...).swap(str) 更好。保存作业。 @updogliu 不会使用移动赋值basic_string&amp; operator= (basic_string&amp;&amp; str) noexcept; 吗? 这个答案不会改变全是空格的字符串。这是失败的。【参考方案5】:

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

#include <cctype>
#include <string>
#include <algorithm>

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。通过这种修改,代码如下所示:

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

【讨论】:

我总是想要一个函数调用来修剪字符串,而不是实现它 对于它的价值,没有必要使用那个 lambda。你可以通过std::isspace:auto wsfront=std::find_if_not(s.begin(),s.end(),std::isspace); @vmrob 编译器不一定那么聪明。做你说的模棱两可:candidate template ignored: couldn't infer template argument '_Predicate' find_if_not(_InputIterator __first, _InputIterator __last, _Predicate __pred) @vmrob 不,你不能。 isspace 有两个重载。而且,在标准库中取函数的地址是从C++20开始的UB。 @vmrob 另一个重载是采用语言环境的重载。不过,::isspace 会在 C++20 之前执行(前提是您包含 C 标头)。实际上,另一个问题是参数应该在输入到 isspace 之前转换为 unsigned char,但这是另一回事。【参考方案6】:

试试这个,它对我有用。

inline std::string trim(std::string& str)

    str.erase(str.find_last_not_of(' ')+1);         //suffixing spaces
    str.erase(0, str.find_first_not_of(' '));       //prefixing spaces
    return str;

【讨论】:

@rgove 请解释一下。 str.find_last_not_of(x) 返回不等于 x 的第一个字符的位置。如果没有字符与 x 不匹配,它只返回 npos。在这个例子中,如果没有后缀空格,它将返回 str.length() - 1 的等价物,本质上是 str.erase((str.length() - 1) + 1).,也就是说,除非我大错特错。 这应该返回 std::string& 以避免不必要地调用复制构造函数。 我很困惑为什么修改返回参数后返回一个副本? @MiloDC 我的困惑是为什么返回一个副本而不是一个参考。返回std::string&amp; 对我来说更有意义。 如果你改变顺序(先去掉后缀空格,再去掉前缀空格)会更有效率。【参考方案7】:

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

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

std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;

【讨论】:

很好 :) 但是,我们的两个解决方案的问题在于它们会修剪两端;不能像这样创建ltrimrtrim 很好,但无法处理带有内部空格的字符串。例如trim(abc def") -> abc,只剩下 abc。 如果您知道不会有任何内部空格,这是一个很好的解决方案! 这很好也很简单,但是当字符串被复制进和复制出std::stringstream时它也很慢。 经典修剪不应该删除内部空格。【参考方案8】:

http://ideone.com/nFVtEo

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

【讨论】:

这是如何工作的:这是一个类似复制的解决方案 - 它找到不是空格的第一个字符的位置(it)和反向:之后只有空格的字符的位置(@ 987654324@) - 之后它返回一个新创建的字符串 == 原始字符串部分的副本 - 基于这些迭代器的部分...【参考方案9】:

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

std::string_view trim(std::string_view s)

    s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
    s.remove_suffix(std::min(s.size() - s.find_last_not_of(" \t\r\v\n") - 1, s.size()));

    return s;

一个不错的选择:

std::string_view ltrim(std::string_view s)

    s.remove_prefix(std::distance(s.cbegin(), std::find_if(s.cbegin(), s.cend(),
         [](int c) return !std::isspace(c);)));

    return s;


std::string_view rtrim(std::string_view s)

    s.remove_suffix(std::distance(s.crbegin(), std::find_if(s.crbegin(), s.crend(),
        [](int c) return !std::isspace(c);)));

    return s;


std::string_view trim(std::string_view s)

    return ltrim(rtrim(s));

【讨论】:

我不确定你在测试什么,但在你的例子中std::find_first_not_of 将返回std::string::npos 和std::string_view::size 将返回4。最小值显然是4,要删除的元素数通过std::string_view::remove_prefix。 gcc 9.2 和 clang 9.0 都能正确处理这个问题:godbolt.org/z/DcZbFH【参考方案10】:

对于空字符串,您的代码假定将 1 加到 string::npos 得到 0。string::npos 的类型为 string::size_type,它是无符号的。因此,您依赖于加法的溢出行为。

【讨论】:

你的措辞好像很糟糕。 有符号的整数溢出行为不好。 1 添加到std::string::npos 必须根据C++ Standard 给出0。所以这是一个很好的假设,绝对可以信赖。【参考方案11】:

被Cplusplus.com黑掉

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 情况。 :-)

【讨论】:

这只是rtrim,不是ltrim ^ 你介意使用 find_first_not_of 吗?修改起来比较容易。【参考方案12】:
s.erase(0, s.find_first_not_of(" \n\r\t"));                                                                                               
s.erase(s.find_last_not_of(" \n\r\t")+1);   

【讨论】:

如果您以相反的顺序执行这些操作并先从右侧进行修剪,然后通过修剪左侧来调用移位,则效率会稍高。【参考方案13】:

我的解决方案基于answer by @Bill the Lizard。

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

const std::string StringUtils::WHITESPACE = " \n\r\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);

【讨论】:

【参考方案14】:

C++11 还附带了一个regular expression 模块,它当然可以用来修剪前导或尾随空格。

可能是这样的:

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

【讨论】:

【参考方案15】:

我的回答是对这篇文章的 top answer 进行了改进,它修剪了控制字符和空格(ASCII table 上的 0-32 和 127)。

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

#include <algorithm>
#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 的参考页面来探索这个选项)。

【讨论】:

std::ptr_fun 已弃用【参考方案16】:

这是我使用的。只需不断从前面删除空间,然后,如果有任何剩余,从后面做同样的事情。

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

【讨论】:

【参考方案17】:

一种优雅的方式可以像

std::string & trim(std::string & str)

   return ltrim(rtrim(str));

并且支持功能实现为:

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;   

一旦你准备好所有这些,你也可以这样写:

std::string trim_copy(std::string const & str)

   auto s = str;
   return ltrim(rtrim(s));

【讨论】:

【参考方案18】:

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

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

【讨论】:

@bmgda 理论上最快的版本可能有这个签名: extern "C" void string_trim ( char ** begin_, char ** end_ ) ... 赶上我的漂移?【参考方案19】:

这是一个用正则表达式修剪的解决方案

#include <string>
#include <regex>

string trim(string str)
    return regex_replace(str, regex("(^[ ]+)|([ ]+$)"),"");

【讨论】:

【参考方案20】:

我想如果你开始询问修剪字符串的“最佳方法”,我会说一个好的实现应该是:

    不分配临时字符串 就地修剪和复制修剪有过载 可以轻松定制以接受不同的验证序列/逻辑

显然有太多不同的方法可以解决这个问题,这绝对取决于您的实际需要。但是,C 标准库在 中仍然有一些非常有用的函数,例如 memchr。 C 仍然被认为是 IO 的最佳语言是有原因的——它的标准库是纯粹的效率。

inline const char* trim_start(const char* str)

    while (memchr(" \t\n\r", *str, 4))  ++str;
    return str;

inline const char* trim_end(const char* end)

    while (memchr(" \t\n\r", 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 \nhello\r \t \n";

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

    system("pause");
    return 0;

【讨论】:

【参考方案21】:

修剪 C++11 实现:

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

【讨论】:

【参考方案22】:
str.erase(0, str.find_first_not_of("\t\n\v\f\r ")); // left trim
str.erase(str.find_last_not_of("\t\n\v\f\r ") + 1); // right trim

【讨论】:

【参考方案23】:

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

#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\r\n  ");
    std::string s2("  \r\nc");
    std::string s3("c \t");
    std::string s4("  \rc ");

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

    assert(s1 == " \t\r\n  ");
    assert(s2 == "  \r\nc");
    assert(s3 == "c \t");
    assert(s4 == "  \rc ");

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

【讨论】:

【参考方案24】:

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

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

【讨论】:

OP 建议的方法也不错——只是有点难以遵循。【参考方案25】:

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

【讨论】:

【参考方案26】:

这是我想出的:

std::stringstream trimmer;
trimmer << str;
trimmer >> str;

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

【讨论】:

嗯;这假设字符串没有内部空格(例如空格)。 OP只说他想修剪左边或右边的空白。【参考方案27】:

这是我的版本:

size_t beg = s.find_first_not_of(" \r\n");
return (beg == string::npos) ? "" : in.substr(beg, s.find_last_not_of(" \r\n") - beg);

【讨论】:

您缺少最后一个字符。长度 +1 解决了这个问题【参考方案28】:

对于不习惯到处写std::、还不熟悉const-正确性、iterators、STL algorithms 等的初学者来说,这是一个易于理解的解决方案...

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

希望对你有帮助...

【讨论】:

【参考方案29】:

上述方法很棒,但有时您希望将函数组合用于您的例程认为是空白的部分。在这种情况下,使用函子来组合操作可能会变得混乱,所以我更喜欢一个简单的循环,我可以为修剪进行修改。这是从此处的 C 版本复制的稍微修改的修剪功能。在此示例中,我正在修剪非字母数字字符。

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

【讨论】:

忘记const char* end ?【参考方案30】:

这个……怎么样?

#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  \n   ";
    std::cout << "-" << trim( str ) << "-\n";
    return 0;


注意:我对 C++ 还是比较陌生,所以如果我在这里偏离基础,请原谅我。

【讨论】:

使用regex 进行修剪有点过头了。 它是否比其他一些选项更占用 CPU 资源? 关于此事的相关问题(cc @user1095108):***.com/questions/68648591/…

以上是关于如何修剪 std::string?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用参数将 std::string 复制到 std::string?

SWIG:如何包装 std::string&(通过引用传递的 std::string)

如何在 C++11 中将 std::string 转换为 std::u32string?

如何摆脱“Intellisense:没有合适的转换函数从“std::string”到“std::string *”存在”错误?

如何初始化名为 params 的结构字段 std::map<std::string, std::string>

如何将“char”转换为“std::string”? [复制]