3

Context:

I am currently writing a date formatting library in C++. For example. the library takes in a formatting string such as: dddd, mmmm d, dddd yyyy and produces a result Tuesday, December 26, Tuesday 2016 given year: 2016, month: 12, date: 26 (dddd stands for weekday, d stands for date in number, mmmm stands for month, yyyy stands for year).

I would like to achieve this by using boost::regex_replace.

What I have tried:

Javascript


In javascript I can easily achieve this by doing:

var pattern = 'dddd, mmmm d, yyyy';
pattern.replace(/(dddd)|(d)|(mmmm)|(yyyy)/gi, function (dddd, d, mmmm, yyyy){
    if (dddd) {
        return 'Tuesday';
    }

    if (d) {
        return '26'
    }

    if (mmmm) {
        return 'December';
    }

    if (yyyy) {
       return '2016';
    }
    return '';
}

C++


boost::regex regex1("(dddd)");
boost::regex regex2("(d)");
boost::regex regex3("(mmmm)");
boost::regex regex4("(yyyy)");
boost::smatch match;

// dddd
if (boost::regex_search(pattern, match, regex1))
{
    pattern = boost::regex_replace(pattern, regex1, "Tuesday");
}

// d
if (boost::regex_search(pattern, match, regex2))
{
    pattern = boost::regex_replace(pattern, regex2, "26");
}

// mmmm
if (boost::regex_search(pattern, match, regex3))
{
        pattern = boost::regex_replace(pattern, regex3, "December");
}

// yyyy
if (boost::regex_search(pattern, match, regex4))
{
    pattern = boost::regex_replace(pattern, regex4, "2016");
}

However, that gives me a result of "Tues26ay, December 26, Tues26ay 2016". The reason is that: as soon as I replace pattern dddd with Tuesday, the d inside Tuesday becomes a target pattern for regex2 causing it to be replaced with 26.

I am not sure how to fix this problem or I think my C++ way for solving this problem is not correct. Is it possible to have something similar as Javascript in C++ boost::regex?

Any help would be appreciated!

Community
  • 1
  • 1

2 Answers2

6

I don't know why you didn't literally 1-on-1 translate:

Live On Coliru

#include <boost/regex.hpp>
#include <iostream>

int main() {
    std::string pattern = "dddd, mmmm d, yyyy";
    pattern = boost::regex_replace(pattern, boost::regex("(dddd)|(d)|(mmmm)|(yyyy)"), [](auto& match)->std::string{
        if (match.str() == "dddd")
            return "Tuesday";

        if (match.str() == "d")
            return "26";

        if (match.str() == "mmmm")
            return "December";

        if (match.str() == "yyyy")
            return "2016";

        return "";
    });

    std::cout << "Result: " << pattern << "\n";
}

Prints

Result: Tuesday, December 26, 2016

Bonus:

sehe
  • 374,641
  • 47
  • 450
  • 633
  • That's great! Thanks for the alternate versions too! Do you think it's possible to get the match's index so a switch could be used? – user Dec 27 '16 at 23:26
  • You can make it so if you want, but it's not there by default. After all, matches don't need to be exclusive (think `"(d(ddd))"`) – sehe Dec 27 '16 at 23:28
  • 2
    @PatLaugh A variant that passes the highest matched group-index to the callback [in c++11](http://coliru.stacked-crooked.com/a/7b8bda8e43a8c11f) or [c++14](http://coliru.stacked-crooked.com/a/6670b386cd83d5f7); here's another [solution that uses four arguments to the formatter callback](http://coliru.stacked-crooked.com/a/4f712f732dad85bb). – sehe Dec 28 '16 at 00:17
1

A simple solution would be to replace the weekday by a control char that's sure not to be elsewhere, then at the end replace that with the proper weekday; I don't have boost, but it works with std::regex.

To add / modify:

boost::regex regex5("(\x01)");
//dddd
pattern = regex_replace(pattern, regex1, "\x01");
//then the fifth check
pattern = regex_replace(pattern, regex5, "Tuesday");
user
  • 675
  • 4
  • 11
  • Yes that should work out as expected. Actually while I was waiting for the answer, I came up with similar ideas as yours except that yours is more safe by using control characters as placeholders. I will mark it as an accepted answer. Thank you. – user2446389 Dec 27 '16 at 21:23
  • Interestingly, you can use a callback for the replacement formatter just like in javascript. See my answer – sehe Dec 27 '16 at 22:22