Saeko
Saeko

Reputation: 441

How to convert char* argv[] to std::regex?

I receive 2 regexes as an input. I am working in C and C++ (we were told that both options are OK, but I am mostly skilled in C). The received regexes have to be applied to a file on stdin, line by line. I use this code:

int res;
line = read_line(&res);
while (res) {
    printf("%s\n",line);
    std::string input_line = line;
    std::string to_replace = argv[1];
    std::string new_regex = argv[2];
    std::regex_replace(input_line, to_replace, new_regex);
    printf("%s\n", input_line);
    free(line)
    line = read_line(&res);
}

But I have problem with the function regex_replace - it seems to take in C++ objects, but I don't know how to convert my input arguments to std::regex. I tried converting it into std::string and then to std::regex, but it didn't work. I would appreciate any help.

Upvotes: 2

Views: 812

Answers (1)

Scheff's Cat
Scheff's Cat

Reputation: 20141

Matthieu already mentioned the doc. of std::regex_replace() on cppreference.com.

There are multiple flavors of std::regex_replace(). Some of them use iterators as arguments. This is very common for std library algorithms because it makes them very flexible to use, i.e. applicable to the broadest variety of things which can be iterated including container templates, arrays, and strings (although there might be additional constraints reducing that).

In this specific case, I choose the (in my case) most convenient flavor:

template< class Traits, class CharT,
          class STraits, class SAlloc >
std::basic_string<CharT,STraits,SAlloc>
    regex_replace( const std::basic_string<CharT,STraits,SAlloc>& s,
                   const std::basic_regex<CharT,Traits>& re,
                   const CharT* fmt,
                   std::regex_constants::match_flag_type flags =
                       std::regex_constants::match_default );

which can be read for my concrete types as:

std::string std::regex_replace(
  const std::string &s, // the input string to process
  const std::regex &re, // the regular expression to apply
  const char *fmt, // the replacement text
  std::regex_constants::match_flag_type flags
    = std::regex_constants::match_default);

The other “missing piece” of OP seems to be the fact that the 2nd argument of regex_replace() is an instance of std::regex.

So, OP's line

std::regex_replace(input_line, to_replace, new_regex);

has three issues:

  1. It doesn't provide a std::regex instance as 2nd argument.
  2. It looks like OP has swapped accidentally the arguments for reg. expression (2nd arg.) and replacement buffer (3rd arg.)
  3. OP missed the fact that the input (given as 1st arg.) isn't changed. Instead the result is provided as return value.

The fix would look like:

input_line
  = std::regex_replace(input_line, std::regex(new_regex), to_replace);

The explicit call of std::regex::regex() is necessary as the doc. of std::regex::regex() shows that:

explicit regex(
  const char *text,
  flag_type f = std::regex_constants::ECMAScript);

as well as

explicit regex(
  const std::string &text,
  flag_type f = std::regex_constants::ECMAScript);

are remarked as explicit i.e. they won't apply automatically for conversion of a given const char* or std::string.

As the regular expression is given once (in command line argument) but might be applied to many lines, I recommend to construct it outside of loop. Regular expressions are not that cheap concerning performance. I often read the hint that pre-compiling is recommended.

Putting all this together, I got the following sample testRE.cc:

#include <iostream>
#include <regex>
#include <string>

int main(int argc, char **argv)
{
  // a minimal check of command line arguments
  if (argc != 3) {
    std::cerr << "ERROR! Wrong number of command line arguments.\n"
      << "Usage:\n"
      << "  " << argv[0] << " REPLACE REGEX\n";
    return 1;
  }
  // configure replace and regex
  const char *const replace = argv[1];
  const std::regex regexpr(argv[2]);
  // process standard input
  for (std::string line; std::getline(std::cin, line);) {
    line = std::regex_replace(line, regexpr, replace);
    std::cout << line << '\n';
  }
  // done
  return 0;
}

Compiled and tested:

$ g++ -std=c++11 -O2 -Wall -pedantic testRE.cc -o testRE

$ (cat | ./testRE "cat" 'd(og|uck)') <<EOF
The quick brown fox jumps over the lazy dog.
My black cat looks to the white duck.
EOF
The quick brown fox jumps over the lazy cat.
My black cat looks to the white cat.

$

Live Demo on coliru


cat__cat___________cat

Con-cat-enation

Upvotes: 2

Related Questions