regex replace with callback in c++11?
I wanted this kind of function and didn't like the answer "use boost". The problem with Benjamin's answer is it provides all the tokens. This means you don't know which token is a match and it doesn't let you use capture groups. This does:
// clang++ -std=c++11 -stdlib=libc++ -o test test.cpp
#include <cstdlib>
#include <iostream>
#include <string>
#include <regex>
namespace std
{
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::regex_iterator<BidirIt> 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);
}
} // namespace std
using namespace std;
std::string my_callback(const std::smatch& m) {
int int_m = atoi(m.str(0).c_str());
return std::to_string(int_m + 1);
}
int main(int argc, char *argv[])
{
cout << regex_replace("my values are 9, 19", regex("\\d+"),
my_callback) << endl;
cout << regex_replace("my values are 9, 19", regex("\\d+"),
[](const std::smatch& m){
int int_m = atoi(m.str(0).c_str());
return std::to_string(int_m + 1);
}
) << endl;
return 0;
}
You could use a regex_token_iterator
#include <iostream>
#include <algorithm>
#include <regex>
#include <string>
#include <sstream>
int main()
{
std::string input_text = "my values are 9, 19";
std::string output_text;
auto callback = [&](std::string const& m){
std::istringstream iss(m);
int n;
if(iss >> n)
{
output_text += std::to_string(n+1);
}
else
{
output_text += m;
}
};
std::regex re("\\d+");
std::sregex_token_iterator
begin(input_text.begin(), input_text.end(), re, {-1,0}),
end;
std::for_each(begin,end,callback);
std::cout << output_text;
}
Note that the {-1,0}
in the argument list of the iterator constructor is a list specifying the submatches we want to iterate over. The -1
is for non-matching sections, and the 0
is for the first submatch.
Also note that I have not used the c++11 regex functionality extensively and am no expert in it. So there may be problems with this code. But for your specific input, I tested it and it seems to produce the expected results. If you find any input set for which it doesn't work, please let me know.
Maybe I arrived too late to this party (about 5 years thought), but I neither liked the answer "use boost", following function has less generalization (speaking about string types), but apparently works. However, I don't know if use a std::ostringstream
is better than std::string::append
:
std::string regex_replace(
const std::string& input,
const std::regex& regex,
std::function<std::string(std::smatch const& match)> format) {
std::ostringstream output;
std::sregex_iterator begin(input.begin(), input.end(), regex), end;
for(; begin != end; begin++){
output << begin->prefix() << format(*begin);
}
output << input.substr(input.size() - begin->position());
return output.str();
}
So, as you can see I used std::sregex_iterator
instead of std::sregex_token_iterator
.