Reputation: 2596
I have text with some lines contains properly escaped double quotes in strings and some not like below:
bla1 "aaa"bbb"ccc" bla1
bla2 "aaa\"bbb\"ccc" bla2
The results after substitution should be
bla1 "aaa\"bbb\"ccc" bla1
bla2 "aaa\"bbb\"ccc" bla2
but not:
bla1 "aaa\"bbb\"ccc" bla1
bla2 "aaa\\"bbb\\"ccc" bla2
In other words it should escape the double quotes in lines where they are not escaped and do not touch lines which are already properly escaped
So far I got the second result with this
%s:\(\s".\+\)\(".\+\)\(".\+"\s\):\1\\\2\\\3:g
Then I've tried a negative lookbehind to tell the engine to not match if there is a backslash before the quotes
(?<!\) which in vim should be something like @<!\
%s:\(\s".\+\)\@<!\\\(".\+\)@<!\\\(".\+"\s\):\1\\\2\\\3:g
But I think I've got a little lost.
Note:
There is only one such string per line
The string is enclosed in double quotes and can contain double quotes inside - only this inside one should be escaped
Upvotes: 3
Views: 4887
Reputation: 31419
Since you said there is only 1 string on each line, you can chain substitute commands to get the result that you want. (This also leads to easier regexes in all parts of the command)
:%s/"\zs.*\ze"/\=substitute(submatch(0), '\\\@<!"', '\\"', 'g')
Explanation:
:%s/"\zs.*\ze"
matches everything on the line between the first and the last quote. We use the greedy .*
to do this. \zs
marks the start of the match and \ze
marks the end of the match.After that we can pass the match to a second substitute command by adding \=
to the start of the replacement. This means that the result of the expression after it will be the replacement string.
substitute(submatch(0), '\\\@<!"', '\\"', 'g')
submatch(0)
is everything between the quotes. We then replace all quotes that don't have a slash before it (\\\@<!"
) with a \"
.
Take a loot at :h sub-replace-expression
, :h /\zs
and :h /\ze
Example Input:
bla1 "aaa"bbb"ccc" bla1
bla2 "aaa\"bbb\"ccc" bla2
bla\bla3 "aaa"bbb"ccc" bla3
blabla4 "aaa"bbb" "BBB"ccc" bla4
bla\bla5 "aaa"bbb" "BBB"ccc" bla5
bla\bla5 "aaa"bbb""BBB"ccc" bla5
Example Output:
bla1 "aaa\"bbb\"ccc" bla1
bla2 "aaa\"bbb\"ccc" bla2
bla\bla3 "aaa\"bbb\"ccc" bla3
blabla4 "aaa\"bbb\" \"BBB\"ccc" bla4
bla\bla5 "aaa\"bbb\" \"BBB\"ccc" bla5
bla\bla5 "aaa\"bbb\"\"BBB\"ccc" bla5
Upvotes: 3
Reputation: 1807
You could just stick an inverse global onto one of the command you wrote already. Now it only will apply to lines that don't contain already escaped quotes:
:v/\\"/s:\(\s".\+\)\(".\+\)\(".\+"\s\):\1\\\2\\\3:g
Upvotes: 1
Reputation: 56049
:%s/\([^ \\]\)"\([^ ]\)/\1\\"\2/g
This finds quotation marks not preceded by a slash or space and not followed by a space.
Upvotes: 0