是否可以创建一个重复执行替换的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"/>
我的想法是执行一个替换,它将第一对点从折线中取出,并在折线之前用第一对和第二对点(如果至少有两个点)创建一条线。这将重复进行,直到找不到更多匹配,即每条折线仅由一对点组成。然后删除剩余的折线。
也许有一个更简单的解决方案,只使用一个替换来执行任务,但我无法想到任何。
答案 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";
}
}