创建Vimscript重复替换直到找不到更多匹配

时间:2017-10-25 00:27:38

标签: regex vim

是否可以创建一个重复执行替换的vimscript,直到找不到更多匹配项?

在我的情况下,我想编辑SVG文件,使它们只包含线条,没有折线或多边形。所以例如我想要字符串

<polyline points="0,1 2,3 4,5 6,7"/>

成为:

<line x1="0" y1="1" x2="2" y2="3"/>
<line x1="2" y1="3" x2="4" y2="5"/>
<line x1="4" y1="5" x2="6" y2="7"/>

我的想法是执行一个替换,它将第一对点从折线中取出,并在折线之前用第一对和第二对点(如果至少有两个点)创建一条线。这将重复进行,直到找不到更多匹配,即每条折线仅由一对点组成。然后删除剩余的折线。

也许有一个更简单的解决方案,只使用一个替换来执行任务,但我无法想到任何。

2 个答案:

答案 0 :(得分:2)

实际上,在没有匹配之前,你甚至不需要重复vimscript!您可以使用普通模式击键执行此操作。这是一个非常有用的技巧。一般来说,你可以做到

qqqqq:s/foo/bar/<cr>@qq@q

说明:

qqq                         " Clear register 'q'
   qq                       " Start recording into register 'q'
     :s/foo/bar/<cr>        " Run the regex
                    @q      " Call macro 'q' (from within the macro 'q')
                      q     " Stop recording 
                       @q   " Call macro 'q'

一旦:s/foo/bar/<cr>不匹配,它将破坏宏并停止运行。

你想要的正则表达式非常复杂:

%s/\v(\<polyline points\=".{-})@<=(\d+),(\d+) (\d+),(\d+)(.*)/\4,\5\6\r<line x1="\2" y1="\3" x2="\4" y2="\5"\/>

(\<polyline points\=".{-})@<=使得该行的其余部分前面有<polyline points=",但该部分与零宽度匹配。然后,我们会查找两个数字(\d+),(\d+) (\d+),(\d+),后跟任何(.*)。然后我们用第一对数字后面的所有数字替换:

\4,\5\6\r<line x1="\2" y1="\3" x2="\4" y2="\5"\/>

\4,\5\6\r是第二对数字,其后是行的其余部分,后跟换行符。之后是你想要的新行。

使用global命令可以更简单:

g/polyline.*,.*,/s/\v(\d+),(\d+) (\d+),(\d+)(.*)/\3,\4\5\r<line x1="\1" y1="\2" x2="\3" y2="\4"\/>

通过仅在匹配(\<polyline points\=".{-})@<=的行上运行正则表达式,即包含折线后跟两个逗号的行,删除了杂乱的polyline.*,.*,

如果您想在vimscript中执行此操作,只需创建一个设置寄存器'q'的函数,然后使用:norm @q调用它。例如:

function! SVGSplit()
  let @q=':%s/\v(\<polyline points\=".{-})@<=(\d+),(\d+) (\d+),(\d+)(.*)/\4,\5\6\r<line x1="\2" y1="\3" x2="\4" y2="\5"\/>'."\<CR>".'@q'
  normal @q
endfunction

然后你可以:call SVGSplit()

无论出于何种原因,这都不适用于:g版本,我不知道为什么。

答案 1 :(得分:2)

要指出这可以在Awk(特别是gawk)和FPAT变量中完成。

gawk -v FPAT='[0-9]+' '{for(i=1;i<=NF-3;i+=2){printf "<line x1=\"" $i "\" y1=\"" $(i+1) "\" x2=\"" $(i+2) "\" y2=\"" $(i+3) "\"/>\n";}}' file.txt

为了便于阅读:

{
    for(i=1;i<=NF-3;i+=2){
        printf "<line x1=\"" $i "\" y1=\"" $(i+1) "\" x2=\"" $(i+2) "\" y2=\"" $(i+3) "\"/>\n";
    }
}