How can I briefly find all the numbers in a string without using a loop?

I want to get all the digits in std::string, but without using a loop (I myself do not mind the code that I call uses). An alternative type of query: delete all lines from a line without a remainder, leaving only numbers. I know that I can find the whole digit in a string using a code like this:

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;
}

However, this function uses a loop. std::stringdoes not provide a function find_all()or anything else! Ideally, the line runs in place (the code above moves it, but it is easy to change it to get a link).

When there are several alternatives, I promise to publish profiling results on how good the different approaches are for some long text.

+4
9

- 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;
}
+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 ( 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, undefined , 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())
  • "sort-approach"
  • copy_if(),
  • text = std::regex_replace(text, std::regex(R"(\D)"), ""); ( icc)
  • 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
+6

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

+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) );
}

save :

std::string save_digits( std::string s ) {
  erase_remove_if( s,
    [](char c){
      if (c > '9') return true;
      return c < '0';
    }
  );
  return s;
}
+2

, ?

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;
}
+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);
}
+1

4 ( , 4 ):

1) , ( )  ... , concatentated

2) std::string.find_first_of(),  ( )

3) std::string.find_last_of(),  ( )

4) std::string:: substr() ,

+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;
}

:

  • sink , elision ++ 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);
}

:

  • input can be any iterable range of characters, not just strings
  • can be encoded as a result of returning the output iterator
  • allows destination container / iterator (including ostream_iterator)
  • with a little love, it can be created to handle unicode characters, etc.

funny example:

#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;
}
+1
source

I use this one-liner if it is before it, #include <regex>or you otherwise include this:

#define DIGITS_IN_STRING(a) std::regex_replace(a, std::regex(R"([\D])"), "")
0
source

Source: https://habr.com/ru/post/1568074/


All Articles