Hisham Hijjawi
Hisham Hijjawi

Reputation: 2415

C++ Regex Replace One by One

Suppose my string is "g500() g600() g200()\n g1()" I want to output "g1() g2() g3()\n g4()" instead.

I have tried using regex to do it like so, but this gives each occurence the same value.

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

void renameGates(std::string& qt_prims, int& gate_id){
    std::string s("g.*?\\(");
    std::regex re(s);  
    qt_prims=std::regex_replace(qt_prims,re,std::string("g"+std::to_string(gate_id)+     "("));
    ++gate_id;
    std::cout<<qt_prims;
    //should print "g1() g2() g3()\n g4()" but prints "g0() g0() g0()\n g0()"
}
int main(){
     std::string str="g500() g600() g200()\n g1()";
     int x=0;
     renameGates(str,x); 
}

But all the values of g get the same value. I have tried reading the c++ documentation on std::regex_replace, but the function signatures are really cryptic. I don't know which version I need.

Upvotes: 0

Views: 1182

Answers (2)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626929

You may implement a callback to regex_replace like this:

#include <iostream>
#include <cstdlib>
#include <string>
#include <regex>
using namespace std;

int gate_id = 0;

template<class BidirIt, class Traits, class CharT, class UnaryFunction>
std::basic_string<CharT> regex_replace(BidirIt first, BidirIt last,
    const std::basic_regex<CharT,Traits>& re, UnaryFunction f)
{
    std::basic_string<CharT> s;

    typename std::match_results<BidirIt>::difference_type
        positionOfLastMatch = 0;
    auto endOfLastMatch = first;

    auto callback = [&](const std::match_results<BidirIt>& match)
    {
        auto positionOfThisMatch = match.position(0);
        auto diff = positionOfThisMatch - positionOfLastMatch;

        auto startOfThisMatch = endOfLastMatch;
        std::advance(startOfThisMatch, diff);

        s.append(endOfLastMatch, startOfThisMatch);
        s.append(f(match));

        auto lengthOfMatch = match.length(0);

        positionOfLastMatch = positionOfThisMatch + lengthOfMatch;

        endOfLastMatch = startOfThisMatch;
        std::advance(endOfLastMatch, lengthOfMatch);
    };

    std::sregex_iterator begin(first, last, re), end;
    std::for_each(begin, end, callback);

    s.append(endOfLastMatch, last);

    return s;
}

template<class Traits, class CharT, class UnaryFunction>
std::string regex_replace(const std::string& s,
    const std::basic_regex<CharT,Traits>& re, UnaryFunction f)
{
    return regex_replace(s.cbegin(), s.cend(), re, f);
}

std::string my_callback(const std::smatch& m) {
    gate_id++;
    stringstream s;
    s << "g" << gate_id << "()";
    return s.str();
}

int main() {
    gate_id = 0;
    std::string s = "g500() g600() g200()\n g1()";
    std::cout << regex_replace(s, regex("g\\S*\\(\\)"), my_callback) << std::endl;
    return 0;
}

See the C++ demo

The pattern is

  • g- a g char
  • \S* - 0+ non-whitespace chars
  • \(\) - a () substring.

The replacement is built dynamically inside my_callback method.

For each subsequent replacement, you need to reset the gate_id.

Upvotes: 1

A M
A M

Reputation: 15267

It will not work as you planned. The regex will replace everything. Or you could use backreferences ($1, $2, $3 and so on), if the number of patterns in the string are known.

You will also have the difficulty with counting. The replace string will be created once and the counter will always have the same value.

So we need to use a different approach using std::regex_search.. We search for our pattern, then take the prefix and add the new g().

Then we continue the operation in a loop with the suffix.

And that's it.

See the following example:

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

void renameGates(std::string& qt_prims, int& gate_id){
    // Define a std::regex to search for g then some digits and brackezs
    std::regex re("g\\d+\\(\\)"); 

    // Here we will receive the submatches
    std::smatch sm{};

    // Make a local copy
    std::string tmp{qt_prims};

    // Reset resulting value
    qt_prims.clear();

    // Search all g-numbers
    while (std::regex_search(tmp, sm, re)) {
        // Build resulting string
        qt_prims = qt_prims + std::string(sm.prefix()) +  "g" + std::to_string(gate_id++) + "()";
        // Continue to search with the rest of the string
        tmp = sm.suffix();
    }
    // If there is still a suffix, add it
    qt_prims += sm.suffix();

    // Debug output
    std::cout << qt_prims << "\n";

}
int main(){
     std::string str="g500() g600() g200()\n g1()";
     int x=0;
     renameGates(str,x); 
}

Upvotes: 1

Related Questions