如何在不使用循环的情况下简明地查找字符串中的所有数字?

Die*_*ühl 4 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.npos != (pos = input.find_first_of("0123456789"));
         ++pos) {
        input[next_digit++] = input[pos];
    }
    input.resize(next_digit);
    return input;
}
Run Code Online (Sandbox Code Playgroud)

但是,此函数使用循环.std::string不提供功能find_all()或东西!理想情况下,字符串就地移动(上面的代码移动它,但很容易更改为参考).

当有多种选择时,我会保证发布不同方法在某些冗长文本上有多好的分析结果.

wak*_*jah 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;
}
Run Code Online (Sandbox Code Playgroud)

显然这在内部使用循环,但你说你不关心那个......

编辑:有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;
}
Run Code Online (Sandbox Code Playgroud)


Die*_*ühl 6

虽然我主要希望得到5个快速答案(没有实现,但感叹),答案和评论导致了一些我没有想过的有趣方法.我个人的期望是有效的答案会导致:

相反,有一些我甚至没有考虑过的方法:

  • 使用递归函数!

  • 使用std::partition()似乎需要额外的工作(保留将被抛出的字符)并更改顺序.

  • 使用std::stable_partition()似乎需要更多工作,但不会改变顺序.

  • 使用std::sort()并提取具有相关字符的子字符串,虽然我不知道如何保留原始的字符序列.只是使用稳定版本并不完全.

将不同的方法放在一起并使用许多关于如何对字符进行分类的变体,导致总共17个版本的大致相同的操作(代码github上).大多数版本的使用std::remove_if()std::string::erase(),但在数字的分类有所不同.

  1. remove_if()[](char c){ return d.find(c) == d.npos; }).
  2. remove_if()[](char c){ return std::find(d.begin(), d.end(), c) == d.end(); }
  3. remove_if()[](char c){ return !std::binary_search(d.begin(), d.end()); }
  4. remove_if()[](char c){ return '0' <= c && c <= '9'; }
  5. remove_if()with [](unsigned char c){ return !std::isdigit(c); }(char传递为unsigned char避免未定义的行为,如果cchar负值)
  6. remove_if()with std::not1(std::ptr_fun(std::static_cast<int(*)(int)>(&std::isdigit)))(必须使用强制转换来确定正确的过载:std::isdigit()恰好是过载).
  7. remove_if()[&](char c){ return !hash.count(c); }
  8. remove_if()with [&](char c){ return filter[c]; }(代码初始化实际上使用循环)
  9. remove_if()[&](char c){ return std::isidigit(c, locale); }
  10. remove_if()[&](char c){ return ctype.is(std::ctype_base::digit, c); }
  11. str.erase(std::parition(str.begin(), str.end(), [](unsigned char c){ return !std::isdigit(c); }), str.end())
  12. str.erase(std::stable_parition(str.begin(), str.end(), [](unsigned char c){ return !std::isdigit(c); }), str.end())
  13. "排序方法"在其中一个答案中描述
  14. copy_if()其中一个答案中描述的方法
  15. 递归方法在其中一个答案中描述
  16. text = std::regex_replace(text, std::regex(R"(\D)"), ""); (我没有设法让它在icc上工作)
  17. 像16,但已经建立了正则表达式

我在MacOS笔记本上运行了基准测试.由于这样的结果很容易用Google Chars绘制图形,因此这里是结果图表(虽然删除了使用正则表达式的版本,因为这些会导致图形缩放,使得有趣的位不可见).表格形式的基准测试结果:

    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
Run Code Online (Sandbox Code Playgroud)