3

Is there a built in way to iterate over non-empty captures only or do I need to use a lambda/modify my regex?

For example, given: const auto input = "Peas&Carrots Spinach-Casserole Beets Pizza Spinach-Salad Coleslaw"s I'd like to find foods that don't contain "Spinach". So I can do this:

const regex re{ "\\s*(?:\\S*Spinach\\S*|(\\S*))" };

copy(sregex_token_iterator(cbegin(input), cend(input), re, 1), sregex_token_iterator(), ostream_iterator<string>(cout, "\n"));

The problem of course is that I get an output like:

Peas&Carrots

Beets
Pizza

Coleslaw

Is there a way around this?

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

3 Answers3

1

The obvious way would be to use std::copy_if (or std::remove_copy_if) and copy the string only if it's non-empty.

remove_copy_if(
    sregex_token_iterator(cbegin(input), cend(input), re, 1),  
    sregex_token_iterator(), 
    ostream_iterator<string>(cout, "\n"),
    [](string const &s) { return s.empty(); } 
);
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

You can use std::copy_if and a lambda to check that the string from the regex match is empty or not. Using

copy_if(sregex_token_iterator(cbegin(input), cend(input), re, 1), 
        sregex_token_iterator(), ostream_iterator<string>(cout, "\n"), 
        [](const std::string& match){ return !match.empty(); });

We get

Peas&Carrots
Beets
Pizza
Coleslaw

Live Example

As it will only print non-empty strings.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
0

From the answers of those wiser than I, it seems that there is in fact no way to discard empty results without a lambda. In this question there are a couple alternatives though:

  1. Use a Look Ahead, which is a bit more expensive, but only captures words without "Spinach":
const regex re{ "(?:\\s+|^)(?!Spinach)(\\S+)" };

copy(sregex_token_iterator(cbegin(input), cend(input), re, 1), sregex_token_iterator(), ostream_iterator<string>(cout, "\n"));

Live Example

  1. Use an istream_iterator and a lambda, this removes a lot of the flexibility of the lambda, but since input is white-space delimited this is probably the best option:
istringstream{ input };

copy_if(istream_iterator<string>(cbegin(input), cend(input)), istream_iterator<string>(), ostream_iterator<string>(cout, "\n"), [](const auto& i) { return i.find("Spinach") == string::npos; });

Live Example

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288