我希望获得std::string
中的所有数字,但没有使用循环(我自己;我正在调用的代码使用的是什么,我不介意)。请求的另一种观点是:从字符串中删除所有非数字,只留下数字。我知道我可以使用以下代码找到字符串中的所有数字:
std::string get_digits(std::string input) {
std::string::size_type next_digit(0u);
for (std::string::size_type pos(0u);
input.npos != (pos = input.find_first_of("0123456789"));
++pos) {
input[next_digit++] = input[pos];
}
input.resize(next_digit);
return input;
}
但是,此函数使用循环。 std::string
未提供函数find_all()
或其他内容!理想情况下,字符串是就地移动的(上面的代码移动它,但很容易更改为参考)。
当有多种选择时,我会保证发布不同方法对某些冗长文本有多好处的分析结果。
答案 0 :(得分:6)
一种方法是使用std::copy_if
(或std::remove_if
):
std::string get_digits(std::string input) {
std::string result;
std::copy_if(
input.begin(),
input.end(),
std::back_inserter(result),
[](char c) { return '0' <= c && c <= '9'; });
return result;
}
显然这在内部使用循环,但是你说你不关心那个......
修改:使用std::remove_if
:
std::string get_digits_remove(std::string input) {
auto itErase = std::remove_if(
input.begin(),
input.end(),
[](char c) { return !('0' <= c && c <= '9'); });
input.erase(itErase, input.end());
return input;
}
答案 1 :(得分:6)
虽然我主要希望得到5个快速答案(没有实现,但感叹),答案和评论导致了一些我没有想到过的有趣方法。我个人的期望是有效的答案会导致:
如果您想要快速,请使用
input.erase(std::remove_if(input.begin(), input.end(),
[](unsigned char c){ return !std::isdigit(c); }),
input.end());
如果您想简明扼要,请使用
text = std::regex_replace(text, std::regex(R"(\D)"), "");
相反,有一些我甚至没有考虑过的方法:
使用递归函数!
使用似乎需要额外工作的std::partition()
(保留将被抛出的字符)并更改顺序。
使用std::stable_partition()
似乎需要更多工作,但不会更改订单。
使用std::sort()
并使用相关字符提取子字符串,但我不知道如何保留原始字符序列。只使用稳定的版本并不完全。
将不同的方法放在一起,并使用了许多关于如何对字符进行分类的变体,导致总共17个版本的大致相同的操作(code在github上)。大多数版本使用std::remove_if()
和std::string::erase()
,但数字分类不同。
remove_if()
与[](char c){ return d.find(c) == d.npos; })
。remove_if()
与[](char c){ return std::find(d.begin(), d.end(), c) == d.end(); }
remove_if()
与[](char c){ return !std::binary_search(d.begin(), d.end()); }
remove_if()
与[](char c){ return '0' <= c && c <= '9'; }
remove_if()
与[](unsigned char c){ return !std::isdigit(c); }
char
作为unsigned char
传递,以避免在c
为char
为负remove_if()
时未定义的行为值)std::not1(std::ptr_fun(std::static_cast<int(*)(int)>(&std::isdigit)))
std::isdigit()
(必须使用强制转换来确定正确的重载:remove_if()
恰好超载了。)[&](char c){ return !hash.count(c); }
与remove_if()
[&](char c){ return filter[c]; }
与remove_if()
(代码初始化实际上使用循环)[&](char c){ return std::isidigit(c, locale); }
与remove_if()
[&](char c){ return ctype.is(std::ctype_base::digit, c); }
与str.erase(std::parition(str.begin(), str.end(), [](unsigned char c){ return !std::isdigit(c); }), str.end())
str.erase(std::stable_parition(str.begin(), str.end(), [](unsigned char c){ return !std::isdigit(c); }), str.end())
copy_if()
text = std::regex_replace(text, std::regex(R"(\D)"), "");
方法 test clang gcc icc
1 use_remove_if_str_find 22525 26846 24815
2 use_remove_if_find 31787 23498 25379
3 use_remove_if_binary_search 26709 27507 37016
4 use_remove_if_compare 2375 2263 1847
5 use_remove_if_ctype 1956 2209 2218
6 use_remove_if_ctype_ptr_fun 1895 2304 2236
7 use_remove_if_hash 79775 60554 81363
8 use_remove_if_table 1967 2319 2769
9 use_remove_if_locale_naive 17884 61096 21301
10 use_remove_if_locale 2801 5184 2776
11 use_partition 1987 2260 2183
12 use_stable_partition 7134 4085 13094
13 use_sort 59906 100581 67072
14 use_copy_if 3615 2845 3654
15 use_recursive 2524 2482 2560
16 regex_build 758951 531641
17 regex_prebuild 775450 519263
(我没有设法让它在icc上工作)我在MacOS笔记本上运行了基准测试。由于使用Google Chars可以很容易地绘制这样的结果,here是结果的图形(尽管删除了使用regexp的版本,因为这些会导致图形缩放,使得有趣的位不可见)。表格形式的基准测试结果:
{{1}}
答案 2 :(得分:2)
您可以使用std::partition
:
std::string get_digits(std::string& input)
{
auto split =
std::partition( std::begin(input), std::end(input), [](char c){return ::isdigit(c);} );
size_t len = std::distance( std::begin(input), split );
input.resize( len );
return input;
}
std::partition
不保证订单,因此如果订单很重要,请使用std::stable_partition
答案 3 :(得分:2)
我会从一个很好的原始函数开始,它组成你想要使用的std
算法:
template<class Container, class Test>
void erase_remove_if( Container&& c, Test&& test ) {
using std::begin; using std::end;
auto it = std::remove_if( begin(c), end(c), std::forward<Test>(test) );
c.erase( it, end(c) );
}
然后我们写保存数字:
std::string save_digits( std::string s ) {
erase_remove_if( s,
[](char c){
if (c > '9') return true;
return c < '0';
}
);
return s;
}
答案 4 :(得分:1)
也许这个简单的答案就足够了?
std::string only_the_digits(std::string s)
{
s.erase(std::remove_if(s.begin(), s.end(),
[](char c) { return !::isdigit(c); }), s.end());
return s;
}
这种方法的缺点是无条件地创建输入数据的副本。如果有很多数字,那就没关系,因为我们正在重用那个对象。或者,您可以使此功能只是就地修改字符串(void strip_non_digits(std::string &)
。)
但是如果只有几个数字而你想保持输入不变,那么你可能更喜欢创建一个新的(小)输出对象而不是复制输入。这可以通过输入字符串的参考视图来完成,例如,由基础TS提供,并使用copy_if
:
std::string only_the_digits(std::experimental::string_view sv)
{
std::string result;
std::copy_if(sv.begin(), sv.end(), std::back_inserter(::isdigit));
return result;
}
答案 5 :(得分:1)
// terrible no-loop solution
void getDigs(const char* inp, char* dig)
{
if (!*inp)
return;
if (*inp>='0' && *inp<='9')
{
*dig=*inp;
dig++;
*dig=0;
}
getDigs(inp+1,dig);
}
答案 6 :(得分:1)
4个步骤中没有循环解决方案(但有错误检查,超过4个语句):
1)使用合适的排序(递增顺序)对字符串进行排序 ...现在所有数字都在一起,连接起来
2)使用std :: string.find_first_of()查找第一个数字的索引 (务必检查找到的数字)
3)使用std :: string.find_last_of()查找最后一位数的索引 (务必检查找到的数字)
4)使用std :: string :: substr()和前面的2个索引来提取数字
答案 7 :(得分:1)
我觉得这很简洁。
std::string get_digits(std::string input)
{
input.erase(std::stable_partition(
std::begin(input),
std::end(input),
::isdigit),
std::end(input));
return input;
}
特点:
这将是基于stl风格的迭代器方法:
template<class InIter, class OutIter>
OutIter collect_digits(InIter first, InIter last, OutIter first_out)
{
return std::copy_if(first, last, first_out, ::isdigit);
}
这有许多优点:
有趣的例子:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
template<class InIter, class OutIter>
OutIter collect_digits(InIter first, InIter last, OutIter first_out)
{
return std::copy_if(first, last, first_out, ::isdigit);
}
using namespace std;
int main()
{
char chunk1[] = "abc123bca";
string chunk2 { "def456fed" };
vector<char> chunk3 = { 'g', 'h', 'i', '7', '8', '9', 'i', 'h', 'g' };
string result;
auto pos = collect_digits(begin(chunk1), end(chunk1), back_inserter(result));
pos = collect_digits(begin(chunk2), end(chunk2), pos);
collect_digits(begin(chunk3), end(chunk3), pos);
cout << "first collect: " << result << endl;
cout << "second collect: ";
collect_digits(begin(chunk3),
end(chunk3),
collect_digits(begin(chunk2),
end(chunk2),
collect_digits(begin(chunk1),
end(chunk1),
ostream_iterator<char>(cout))));
cout << endl;
return 0;
}
答案 8 :(得分:0)
只要#include <regex>
出现在我之前,我就会使用这个单线宏,或者你包含它:
#define DIGITS_IN_STRING(a) std::regex_replace(a, std::regex(R"([\D])"), "")