我目前正在使用以下代码来修正std::strings
程序中的所有内容:
std::string s; s.erase(s.find_last_not_of(" \n\r\t")+1);
它工作正常,但我想知道是否有一些可能会失败的最终案例?
当然,欢迎使用优雅替代品和左侧解决方案的答案.
编辑从c ++ 17开始,删除了标准库的某些部分.幸运的是,从c ++ 11开始,我们有lambda这是一个很好的解决方案.
#include#include #include // 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; }
感谢/sf/ask/17360801/提出现代解决方案.
我倾向于使用这些中的一个来满足我的修剪需求:
#include#include #include #include // trim from start static inline std::string <rim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun (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 (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#include #include #include // 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 (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 (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的字符串算法是最简单的:
#includestd::string str("hello world! "); boost::trim_right(str);
str
现在"hello world!"
.还有trim_left
和trim
,其修剪两侧.
如果您_copy
为上述任何函数名称添加后缀,例如trim_copy
,该函数将返回字符串的修剪副本,而不是通过引用修改它.
如果_if
为上述任何函数名添加后缀,例如trim_copy_if
,您可以修剪满足自定义谓词的所有字符,而不仅仅是空格.
使用以下代码从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 ); }
派对迟到了,但没关系.现在C++ 11在这里,我们有lambdas和auto变量.所以我的版本,也处理所有空格和空字符串,是:
#include#include #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至少不够智能来推断反向迭代器的类型(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()); }
你在做什么是好的和强大的.我已经使用了相同的方法很长一段时间,我还没有找到一个更快的方法:
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); }
通过提供要修剪的字符,您可以灵活地修剪非空白字符,并且只能修剪您想要修剪的字符.
试试这个,它对我有用.
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()
std::stringstream trimmer; trimmer << str; str.clear(); trimmer >> str;
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()); }
在空字符串的情况下,您的代码假定添加1 string::npos
给出0. string::npos
是类型string::size_type
,它是无符号的.因此,您依赖于添加的溢出行为.
被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情况.:-)
我的解决方案基于@Bill蜥蜴的回答.
请注意,如果输入字符串只包含空格,则这些函数将返回空字符串.
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 ++ 17中,您可以使用basic_string_view :: remove_prefix和basic_string_view :: remove_suffix:
std::string_view trim(std::string_view s) const { s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size())); s.remove_suffix((s.size() - 1) - std::min(s.find_last_not_of(" \t\r\v\n"), s.size() - 1)); return s; }
我的答案是对这篇文章的最佳答案的改进,它修剪了控制字符和空格(ASCII表上的0-32和127 ).
std::isgraph
确定一个角色是否具有图形表示,因此您可以使用它来改变Evan的答案,以从字符串的任一侧删除任何没有图形表示的字符.结果是一个更优雅的解决方案:
#include#include #include /** * @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 (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 (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
以探索此选项) .
使用C++ 11还有一个正则表达式模块,当然可以用来修剪前导或尾随空格.
也许是这样的:
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)); }
这就是我使用的.只需继续从前面移除空间,然后,如果还有任何东西,请从背面做同样的事情.
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字符串和索引,而不是使用迭代器和std :: finds.它优化了以下特殊情况:size 0 string(什么都不做),没有要修剪的空格的字符串(什么都不做),只有尾随空格的字符串要修剪(只调整字符串大小),字符串完全是空格(只是清除字符串) .最后,在最坏的情况下(带有前导空格的字符串),它会尽最大努力执行有效的复制构造,只执行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)); } }
s.erase(0, s.find_first_not_of(" \n\r\t")); s.erase(s.find_last_not_of(" \n\r\t")+1);
这样做的优雅方式可能就像
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(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 (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)); }
修剪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()); }