VIM用多种模式替换

时间:2016-07-14 21:20:49

标签: regex vim awk replace sed

我需要修改一组看起来像这样的字符串:

debug("some random text:  val " + arg1);

debug("some random text:  val " + arg1 + " val2 " + arg2);

debug("some random text:  val " + getValue(1) + " val2" + getValue(2) + ".");

或其任何类似组合。例如,可能有0个或更多个args'所有都需要更换

debug("some random text:  val ", arg1);

debug("some random text:  val {} val2 {}", arg1, arg2);

debug("some random text:  val {} val2 {}.", getValue(1), getValue(2));

基本上将每个匹配+。* +的参数替换为{}并将匹配的参数放在"的末尾。引用字符串"用逗号和相同的顺序。 所以结果的一般形式应该是这样的:

("this is a quoted {} string with {} tokens {} in it", arg1, arg2, arg);

2 个答案:

答案 0 :(得分:1)

最简单的方法是分多步完成。以下方法中所需的步骤数等于任何单个调试语句中的最大参数数加一。

示例文本文件:

debug("some random text:  val " + arg1);
debug("some random text:  val " + arg1 + " val2 " + arg2);
debug("some random text:  val " + getValue(1) + " val2" + getValue(2) + ".");

我们首先在必要时附加一个空字符串文字。

:g/^\s*debug("/s/[^"[:blank:]]\s*\zs\ze);/ + ""/

注意第1行和第2行是如何受到影响的,但第3行不受影响。那是故意的;第3行已经以字符串文字结尾。

debug("some random text:  val " + arg1 + "");
debug("some random text:  val " + arg1 + " val2 " + arg2 + "");
debug("some random text:  val " + getValue(1) + " val2" + getValue(2) + ".");

接下来,我们将每个第一个参数移动到位。

:g/^\s*debug("/s/"\s*+\([^+]\{-}\)\s*\(+\s*"\(.*\)\)\?\ze);/{}\3,\1/

注意第二个(和以下)参数如何不受影响。

debug("some random text:  val {}", arg1);
debug("some random text:  val {} val2 " + arg2 + "", arg1);
debug("some random text:  val {} val2" + getValue(2) + ".", getValue(1));

重复相同的命令。最简单的方法是按@:

debug("some random text:  val {}", arg1);
debug("some random text:  val {} val2 {}", arg1, arg2);
debug("some random text:  val {} val2{}.", getValue(1), getValue(2));

对具有两个以上参数的行重复。一旦命令不再影响任何行,就完成了。

注意:

  • 我是在假设每个调试语句(包括所有参数)占用一行的情况下做到的。
  • 使用的正则表达式可能需要对更复杂的参数表达式进行一些调整(例如,表达式本身包含+运算符)。
  • 建议您之后使用diff工具手动验证结果。

答案 1 :(得分:0)

对于相对“封闭”的解决方案(您仍需要多次执行多个参数的命令),重复此命令直到不再有更改:

(例如,单行):

:s/\([^"]*"[^"]*\)"\s*+\s*\([^ ,]\+\)\(\s*+\s*"\)\=\(\([^"]*\)"\)\=\(.*\)\([^"]*\));/\1{}\5"\6, \2\7);/

说明:
该表达式将代码分解为以下九个元素,包括七个带括号的元素(\1,...,\7,由开括号的顺序标识)和两个非括号的元素(列出如下--

/\([^"]*"[^"]*\)"\s*+\s*\([^ ,]\+\)\(\s*+\s*"\)\=\(\([^"]*\)"\)\=\(.*\)\([^"]*\));/
   ----\1-----            --\2---    ---\3---        -\5--         \6    -\7--                                                          
                                                   ----\4----

\1: [^"]*"[^"]\*    " Up to second quote (i.e. up to first closing quote)
--: "\s*+\s*        " The first closing quote and '+' operator
\2: [^ ,]\+         " The first concatenated arg (to be converted into an argument list element)
\3: \s*+\s*"        " The following '+' operator and opening quote of the second string (if present) - discard
\4: \([^"]*\)"      " The second string (if present), so we can close the quotes on our (newly combined?) first string
\5:                 " (Embedded in \4 above) - the second string minus the closing quote (the part we want)
\6: .*              " Everything up to the final quote (if there are any more); no more quotes after this
\7: [^"]*           " Everything else (up to closing paren)
--: );              " The end

列为“如果存在”等的元素或子元素使用\=限定。我们使用\1\2(在新位置),\5\6\7来构建我们想要的结果。

在@ Ruud的答案中添加一个范围或限定模式,以便一次执行多行(我倾向于使用范围%,如:%s///中的整个文件,但这也可能与一些非预期的线相匹配。)

@Ruud提到的所有相同的警告也适用于此 - 假设每行上的每个语句,可能不会解释参数中更复杂的表达式等。

与多步骤或脚本方法相比,像这样的表达式是一项非常多的工作来提出和调试,但它确实有趣的练习=)。