C++ 如何在不使用循环的情况下简洁地查找字符串中的所有数字?
我想获取C++ 如何在不使用循环的情况下简洁地查找字符串中的所有数字?,c++,c++11,c++14,C++,C++11,C++14,我想获取std::string中的所有数字,但不使用循环(我自己;我调用的代码使用什么,我不介意)。请求的另一种视图是:从字符串中删除所有非数字,只保留数字。我知道我可以使用如下代码查找字符串中的所有数字: std::string get_digits(std::string input) { std::string::size_type next_digit(0u); for (std::string::size_type pos(0u); input.npo
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()
或其他功能!理想情况下,字符串是在适当的位置进行操纵的(上面的代码会移动它,但很容易将其更改为引用)
当有多个备选方案时,我将承诺发布分析结果,说明不同方法在某些冗长文本上的效果如何。一种方法是使用
std::copy_if
(或std::remove_if
):
也许简单的答案就足够了
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\u non\u digits(std::string&)
)
但是,如果只有几个数字,并且您希望保持输入不变,那么您可能更喜欢创建一个新的(小的)输出对象,而不是复制输入。这可以通过输入字符串的参考视图来完成,例如,由TS提供,如果,则使用copy\u:
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::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;
}
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;
}
您可以使用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\u partition
//糟糕的无循环解决方案
// 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);
}
void getDigs(常量字符*inp,字符*dig)
{
如果(!*inp)
返回;
如果(*inp>='0'&&&*inp在4个步骤中没有循环解决方案(但有错误检查,超过4条语句):
1) 使用合适的排序(递增顺序)对字符串进行排序
…现在所有的数字都在一起,连在一起
2) 使用std::string.find_first_of()查找第一个数字的索引
(请务必检查是否找到数字)
3) 使用std::string.find_last_of()查找最后一位的索引
(请务必检查是否找到数字)
4) 使用std::string::substr()和前面的两个索引来提取数字我认为这是我所能得到的最简洁的数字
std::string get_digits(std::string input)
{
input.erase(std::stable_partition(
std::begin(input),
std::end(input),
::isdigit),
std::end(input));
return input;
}
特点:
按值传递sink参数,以利用c++11中的复制省略
保留数字的顺序
无用户代码-仅使用同行评审的stl函数。出现错误的可能性为零
这将是基于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);
}
模板
OutIter收集\u位(先初始化、后初始化、先出)
{
返回std::copy_if(first、last、first_out、::isdigit);
}
这有许多优点:
输入可以是任意范围的字符,而不仅仅是字符串
可以通过返回输出迭代器来链接
允许目标容器/迭代器(包括ostream_迭代器)
只要有一点爱,它就可以处理unicode字符等
有趣的例子:
#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;
}
#包括
#包括
#包括
#包括
#包括
模板
OutIter收集\u位(先初始化、后初始化、先出)
{
返回std::copy_if(first、last、first_out、::isdigit);
}
使用名称空间std;
int main()
{
char chunk1[]=“abc123bca”;
字符串chunk2{“def456fed”};
向量chunk3={'g','h','i','7','8','9','i','h','g'};
字符串结果;
自动位置=收集数字(开始(chunk1)、结束(chunk1)、返回插入器(结果));
pos=收集数字(开始(chunk2)、结束(chunk2)、pos);
收集数字(开始(chunk3)、结束(chunk3)、pos);
cout虽然我最初希望有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个版本的大致相同的操作(打开)。大多数版本使用std::remove_if()
和std::string::erase()
,但数字分类不同
使用[](字符c){返回d.find(c)=d.npos;})删除\u if()
remove_if()
with[](字符c){return std::find(d.begin(),d.end(),c)=d.end();}
remove_if()
使用[](字符c){return!std::binary_search(d.begin(),d.end());}
remove_if()
与[](char c){return'0'我将从一个很好的原语函数开始,该函数构成您想要使用的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) );
}
模板
如果(容器和c、测试和测试)无效删除{
使用std::begin;使用std::end;
自动it=std::删除_if(开始(c)、结束(c)、std::转发(测试));
c、 擦除(它,结束(c));
}
然后我们写入保存数字:
std::string save_digits( std::string s ) {
erase_remove_if( s,
[](char c){
if (c > '9') return true;
return c < '0';
}
);
return s;
}
std::string保存\u位(std::string s){
如果出现以下情况,请删除,
[](字符c){
如果(c>'9')返回true;
std::string save_digits( std::string s ) {
erase_remove_if( s,
[](char c){
if (c > '9') return true;
return c < '0';
}
);
return s;
}
#define DIGITS_IN_STRING(a) std::regex_replace(a, std::regex(R"([\D])"), "")