c++ - trim用法 - std string substr




修剪std:: string的最佳方法是什么? (20)

我目前使用下面的代码来修剪我的程序中的所有std::strings

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

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

当然,欢迎使用优雅的替代方案和左修剪解决方案。


C ++ 11:

int i{};
string s = " h e ll \t\n  o";
string trim = " \n\t";

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

cout << s;

输出:

hello

对于空字符串也可以正常工作


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

一个优雅的做法可以是这样的

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

上述方法非常棒,但有时候您希望将函数组合用于您的例程认为是空白的内容。 在这种情况下,使用仿函数来组合操作会变得麻烦,所以我更喜欢简单的循环,我可以修改修剪。 这里是一个稍微修改的修剪功能,从SO上的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* 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 (left & right)
inline std::string& trim(std::string& s, const char* t = ws)
{
    return ltrim(rtrim(s, t), t);
}

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


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

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


侵犯了Cplusplus.com

string choppa(const string &t, const string &ws)
{
    string str = t;
    size_t found;
    found = str.find_last_not_of(ws);
    if (found != string::npos)
        str.erase(found+1);
    else
        str.clear();            // str is all whitespace

    return str;
}

这也适用于null情况。 :-)


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

在空字符串的情况下,你的代码假设给string::npos加1会得到0. string::npos的类型是string::size_type ,它是无符号的。 因此,您依赖于加法的溢出行为。


对于它的价值,这里是一个关注性能的修剪实现。 它比我见过的许多其他修剪程序快得多。 它不使用迭代器和std ::查找,而是使用原始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));
    }
}

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

要纠正这一缺陷,请在两条微调线之间添加一个str.clear()

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

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

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

显然有太多不同的方法来解决这个问题,它绝对取决于你实际需要的东西。 但是,C标准库在<string.h>中仍然有一些非常有用的函数,就像memchr一样。 为什么C仍然被认为是IO的最佳语言是有原因的 - 它的stdlib是纯粹的效率。

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

我的解决方案基于@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);
}

晚会晚了,但没关系。 现在C ++ 11在这里,我们有lambda和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至少不够聪明以推断反向类型迭代器( std::string::const_reverse_iterator )与auto 。 我不知道构建一个反向迭代器是多么昂贵,所以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());
}

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

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

试试这个,它适用于我。

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

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

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

这是我使用的。 只要继续从前面去除空间,然后,如果还有剩下的东西,就从后面做同样的事情。

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
}

这里有一个易于理解的解决方案,用于不习惯编写std:: everywhere并且还不熟悉const正确性, iterator ,STL algorithm等的初学者。

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

希望能帮助到你...


那这个呢...?

#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 ++还比较陌生,所以请原谅我,如果我不在这里。





stdstring