我目前正在使用以下代码在我的程序中右修剪所有std::字符串:
std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);
它工作得很好,但我想知道是否有一些最终情况下它可能会失败?
当然,有优雅的选择和左修剪解决方案的答案是受欢迎的。
我目前正在使用以下代码在我的程序中右修剪所有std::字符串:
std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);
它工作得很好,但我想知道是否有一些最终情况下它可能会失败?
当然,有优雅的选择和左修剪解决方案的答案是受欢迎的。
当前回答
上面的方法很棒,但有时您想要使用函数组合来处理例程认为是空白的部分。在这种情况下,使用函子组合操作可能会变得混乱,所以我更喜欢一个简单的循环,我可以修改修剪。这里是一个稍微修改的修剪函数,从C版本复制到这里的SO。在这个例子中,我正在修剪非字母数字字符。
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);
}
其他回答
For what it's worth, here is a trim implementation with an eye towards performance. It's much quicker than many other trim routines I've seen around. Instead of using iterators and std::finds, it uses raw c strings and indices. It optimizes the following special cases: size 0 string (do nothing), string with no whitespace to trim (do nothing), string with only trailing whitespace to trim (just resize the string), string that's entirely whitespace (just clear the string). And finally, in the worst case (string with leading whitespace), it does its best to perform an efficient copy construction, performing only 1 copy and then moving that copy in place of the original string.
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));
}
}
有点晚了,不过没关系。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至少不足以用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());
}
从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) {
rtrim(s);
ltrim(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提供的现代解决方案。
最初的回答:
我倾向于使用这3种中的一种来满足我的装饰需求:
#include <algorithm>
#include <functional>
#include <cctype>
#include <locale>
// 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<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) {
rtrim(s);
ltrim(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;
}
我保留了上面的原始答案,但是为了上下文和保持高投票的答案仍然可用。
从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;
}
这也适用于空情况。: -)
我不确定您的环境是否相同,但在我的环境中,空字符串情况将导致程序中止。我要么用if(!s.empty())包装这个擦除调用,要么像前面提到的那样使用Boost。