Regex to replace block comment with line comment

Viewed 316

There are tons of examples to do the conversion from C-style line comment to 1-line block comment. But I need to do the opposite: find a regex to replace multi-line block comment with line comments. From:

This text must not be touched
/*
This
is
random
text
*/
This text must not be touched

To

This text must not be touched
// This
// is
// random
// text
This text must not be touched

I was thinking if there's a way to represent "each line" concept in regex, then just add // in front of each line. Something like

\/\*\n(?:(.+)\n)+\*\/ -> // $1

But the greediness nature of the regex engine makes $1 just match the last line before */. I know Perl and other languages have some advanced regex features like recursion, but I need to do this in a standard engine. Is there any trick to accomplish this?

EDIT: To clarify, I'm looking for pure regex solution, not involving any programming language. Should be testable on sites like https://regex101.com/.

2 Answers

If you are interested in a single regex pass in the modern JavaScript engine (and other regex engines supporting infinite length patterns in lookbehinds), you can use

/(?<=^(\/)\*(?:(?!^\/\*)[\s\S])*?\r?\n)(?=[\s\S]*?^\*\/)|(?:\r?\n)?(?:^\/\*|^\*\/)/gm

Replace with $1$1, see the regex demo.

Details

  • (?<=^(\/)\*(?:(?!^\/\*)[\s\S])*?\r?\n) - a positive lookbehind that matches a location that is immediately preceded with
    • ^(\/)\* - /* substring at the start of a line (with / captured into Group 1)
    • (?:(?!^\/\*)[\s\S])*? - any char, zero or more occurrences, as few as possible, not starting a /* char sequence that appears at the start of a line
    • \r?\n - a CRLF or LF ending
  • (?=[\s\S]*?^\*\/) - a positive lookahead that requires any 0 or more chars as few as possible followed with */ at the start of a line, immediately to the right of the current location
  • | - or
  • (?:\r?\n)? - an optional CRLF or LF linebreak
  • (?:^\/\*|^\*\/) - and then either /* or */ at the start of a line.

As usual in such cases, two regular expressions—the second applied to the matches of the first—can do what one cannot achieve.

const txt = `This text must not be touched
/*
This
is
random
text
*/
This text must not be touched`;

const to1line = str => str.replace(
    /\/\*\s*(.*?)\s*\*\//gs,
    (_, comment) => comment.replace( /^/mg, '//')
);
console.log( to1line( txt ));

Related