Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在不使用循环的情况下简洁地查找字符串中的所有数字?_C++_C++11_C++14 - Fatal编程技术网

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])"), "")