在vim中用于转义引号的替换模式

3

我有一些文本,其中某些行包含在字符串中正确转义的双引号,而其他行则没有,例如:

bla1 "aaa"bbb"ccc" bla1
bla2 "aaa\"bbb\"ccc" bla2

替换后的结果应该是:
bla1 "aaa\"bbb\"ccc" bla1
bla2 "aaa\"bbb\"ccc" bla2

但不包括:

bla1 "aaa\"bbb\"ccc" bla1
bla2 "aaa\\"bbb\\"ccc" bla2

换句话说,它应该转义那些没有被转义的双引号,并且不要触碰已经被正确转义的行。目前,我用下面的方法得到了第二个结果。
%s:\(\s".\+\)\(".\+\)\(".\+"\s\):\1\\\2\\\3:g

然后我尝试使用负回顾后断言来告诉引擎,在引号前面有反斜杠的情况下不匹配。

(?<!\) which in vim should be something like @<!\

%s:\(\s".\+\)\@<!\\\(".\+\)@<!\\\(".\+"\s\):\1\\\2\\\3:g

但我想我有点迷失了。

注意:
每行只有一个此类字符串 该字符串被双引号包围,内部可以包含双引号 - 只有内部的双引号需要转义

4个回答

3

从你说每行只有1个字符串来看,你可以使用连续的替换命令来获得所需的结果。(这也会导致命令中所有部分的正则表达式更容易)

:%s/"\zs.*\ze"/\=substitute(submatch(0), '\\\@<!"', '\\"', 'g')

解释:

  1. :%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.
  2. 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 \".

请查看:h sub-replace-expression:h /\zs:h /\ze


示例输入:

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

示例输出:

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

嗨,这个答案比Jeff的好,我已经修改了输入输出数据以显示它即使在字符串内部有多个引号时也可以工作。我打算接受它。只有一个小情况它不能按照我想要的方式工作。 例如: bla\bla5 "aaa"bbb""BBB"ccc" bla5 变成 bla\bla5 "aaa"bbb""BBB"ccc" bla5情况是如果“”内有两个双引号 也许你知道如何使它也适用于这种情况 - szydan
抱歉我按下回车键太早了,现在注释应该没问题了。 - szydan
@szydan,我在第二个替换命令中改变了正则表达式,使用了负向回顾后发现它可以解决两个引号相邻的情况。 - FDinoff

1
你可以在已经编写的命令中添加一个反向全局标记。现在它只适用于不包含已转义引号的行:
:v/\\"/s:\(\s".\+\)\(".\+\)\(".\+"\s\):\1\\\2\\\3:g

似乎可以工作。请问您能否更详细地解释一下这个第一部分v/\/吗? - szydan
查看:h global。 使用v代替g,将命令应用于不匹配模式的行,该模式在本例中只是一个反斜杠。 - Jeff
谢谢,这个技巧不错,但是在像这样的情况下会失败: bla\bla3 "aaa"bbb"ccc" bla3 在实际字符串之前的 blabla 中有一个反斜杠。 - szydan

0
:%s/\([^ \\]\)"\([^ ]\)/\1\\"\2/g

这将查找未在斜杠或空格之前以及未在空格之后的引号。


Kevin,这个方法不起作用 - 我已经在问题中添加了一个注释。 重要的部分是我只想转义那些在一对双引号内部的双引号,而不是所有的双引号。 - szydan

0

这个可能会起作用:

%s/\([^\\]\)\("\)/\1\\\2/g

首先,您不需要捕获引号,因为您知道\2始终只有一个,其次,这也会获取外部引号。 - Kevin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接