If you ignore the content-dependent problem, then it is possible to do replacement with PCRE regex. (It is possible to patch it on case-by-case basis, if the $
which doesn't denote the portion to preserve \
has a non-ambiguous form).
Assumes that $
always starts and ends a non-replacement region, except for the case of the odd last $
in the string.
Pattern (the first line is RAW regex, the second line is quoted string literal):
\G((?:[^$\\]|\$[^$]*+\$|\$(?![^$]*+\$))*+)\\
"\\G((?:[^$\\\\]|\\$[^$]*+\\$|\\$(?![^$]*+\\$))*+)\\\\"
Replace string:
\1"
"\\1\""
DEMO 1
DEMO 2
Explanation
The idea is to find the next \
in the string that is not contained within 2 $
. This is achieved by make sure the match always starts from where the last match left off \G
, to ensure we don't skip over any literal $
and match the \
inside.
There are 3 forms of sequences that we don't replace:
- Is NOT either literal
$
or literal \
: [^$\\]
- Any text in between 2
$
(this doesn't take into account escaping mechanism, if any): \$[^$]*+\$
- Allow replacement of
\
after the odd last $
: \$(?![^$]*+\$)
So we just march through any combination of the 3 forms of sequences above, and match the nearest \
for replacement.
Same assumption as above, except that $<digit>
will not start a non-replacement region.
This will work even with this kind of string:
I have $50 to \spend\. I just $\bar$ remembered that I have another $30 dollars $\left$ from my last \paycheck\. Lone $ \at the end\
Pattern:
\G((?:[^$\\]|\$\d|\$(?![^$]*\$)|\$[^$]*+\$)*+)\\
"\\G((?:[^$\\\\]|\\$\\d|\\$(?![^$]*\\$)|\\$[^$]*+\\$)*+)\\\\"
DEMO
\$\d
is added in front of the \$[^$]*+\$
in alternation to make the engine check for that case first.