Move to 230.00
Hold
Hold
Hold
Hold
Hold
Hold
Move to 00.00
Hold
Hold
Hold
Hold
Hold
FooBar
Hold
Spam
Hold
我希望它看起来像这样:
Move to 230.00
Hold
Move to 00.00
Hold
FooBar
Hold
Spam
Hold
我敢肯定,vim必须有一种方法可以快速地做到这一点,但是我不能完全理解。这是否超出了宏的功能,并且需要vimscript?
此外,如果必须将相同的宏应用于“ Holds”的每个块也可以。它虽然不是一个宏就可以获取整个文件,但那太棒了。
#1 楼
我认为以下命令应该起作用: :%s/^\(.*\)\(\n\)\+$//
说明:
我们在整个文件上使用替换命令将
pattern
更改为string
: :%s/pattern/string/
这里
pattern
是^\(.*\)\(\n\)\+$
和string
是
。pattern
可以这样分解:^\(subpattern1\)\(subpattern2\)\+$
^
和$
分别匹配行首和行尾。 \(
和\)
用来封装subpattern1
,以便以后可以用特殊编号
引用它。它们也用来封装
subpattern2
,以便我们可以重复1个或多个\+
是subpattern1
是.*
是与除换行符之外的任何字符匹配的元字符,而.
是与最后一个字符匹配0、1或更多次的量词。匹配任何不包含新行的文本。 *
是.*
subpattern2
匹配换行,并且\n
匹配第一个\n
内的匹配文本,这里是
。因此,
\(
可以像读取这是:行的开头(\)
),然后是不包含新行的任何文本(subpattern1
),然后是新行(pattern
),然后是同一文本(^
),后两个重复一次或多次(.*
),并且如果
\n
被匹配(同一行块),则替换命令将其替换为
,这里是\+
(该块的第一行)。如果要在不更改文件内容的情况下查看将影响哪些行,可以启用
$
选项并在命令末尾添加pattern
替换标志::%s/^\(.*\)\(\n\)\+$//n
要进行更精细的控制,您还可以在更改每行代码之前要求确认,方法是添加
string
替代标志::%s/^\(.*\)\(\n\)\+$//c
有关替代命令的更多信息阅读
,了解替代标志
hlsearch
,了解各种元字符和量词,阅读
n
,了解vim中的正则表达式。通过在
c
的末尾添加:help :s
来解决命令中的问题。BloodGain具有相同命令的更短且更易读的版本。
#2 楼
请尝试以下操作::%s;\v^(.*)(\n)+$;;
和saginaw的答案一样,它使用Vim的:substitute命令。但是,它利用了几个额外的功能来提高可读性:
Vim允许我们使用任何非字母数字ASCII字符,但反斜杠(\),双引号(“),或用竖线(|)分隔我们的匹配/替换/标志文本。在这里,我选择了分号(;),但您可以选择另一个。
Vim为正则表达式提供“魔术”设置,以便将字符解释为它们的特殊含义而不需要反斜杠转义。这有助于减少冗长,并且比默认的“ nomagic”更一致。以
\v
开头表示“非常魔术”,或除字母数字(A-z0-9之外的所有字符) )和下划线(_)具有特殊含义。组件的含义是:
占整个文件的百分比
s替代
;开始替代字符串
\ v“ very magic”
^行首
(。 *)0个或多个任何字符(第1组)
(\ n \ 1)+换行符后跟(第1组匹配文本),一次或多次(第2组)
$行尾(或在这种情况下,认为下一个字符必须为换行符)
;开始替换字符串
\ 1组1个匹配文本
;命令结束或开始标志
评论
我真的很喜欢您的答案,因为它的可读性更好,而且还因为它使我更好地了解了\ n和$之间的区别。 \ n在模式中添加了一些内容:字符换行,告诉vim以下文本在换行上。尽管$不会向模式中添加任何内容,但是如果模式之外的下一个字符不是换行符,则它只是禁止进行匹配。至少,这是我通过阅读您的答案和:help零宽度所了解的内容。
– saginaw
2015年11月6日14:35
对于^来说也必须如此,它不会向模式中添加任何内容,只会阻止模式之外的上一个字符不是换行符时进行匹配...
– saginaw
2015年11月6日14:37
@saginaw您完全正确,这是一个很好的解释。在正则表达式中,某些字符可以作为控制字符。例如,+的意思是“重复前面的表达式(字符或组)1次或更多次”,但其自身不匹配。 ^表示“不能在字符串的中间开始”,$表示“不能在字符串的中间结束”。请注意,我不是在说“线”,而是在这里说“弦”。 Vim默认把每一行都当作一个字符串-这就是\ n的所在。它告诉Vim消耗一个换行符来尝试匹配。
–充血
15年11月6日,18:52
#3 楼
如果要删除所有相邻的相同行,而不仅仅是Hold
,则可以使用vim
内部的外部过滤器极其轻松地实现: />如果您想直接在:%!uniq
中进行操作,实际上非常棘手。我认为有办法,但是对于一般情况来说,使其100%起作用是非常棘手的,而且我还没有弄清所有的错误。您可以直观地看到非重复的下一行不是以相同的字符开头,可以使用::+,./^[^H]/-d
vim
表示当前行。的。指当前行。 +
表示(/^[^H]/-
)下一行不以H开头的行。然后d是delete。
评论
尽管替代命令和全局Vim命令都是不错的练习,但我将如何解决这一问题(从vim内部或使用shell)调用uniq。一方面,我很确定uniq会将空白/所有空格的行等效处理(未测试过),但是用正则表达式捕获起来会困难得多。这也意味着在我要完成工作时不要“重新发明轮子”。
–充血
2015年11月6日19:06
通过外部工具提供文本的功能是为什么我通常在Windows上推荐Vim和Cygwin的原因。 Vim和Shell只是在一起。
–DevSolar
2015年12月3日,10:33
#4 楼
基于Vim的答案::%s/\(^.*\n\)\{1,}/
=用同一行替换每一行,其后至少要替换一次。
#5 楼
假设Vim 7.4.218或更高版本,还有一个:function! s:Uniq(line1, line2)
let cursor = getcurpos()
let lines = uniq(getline(a:line1, a:line2))
if setline(a:line1, lines) == 0 && len(lines) <= a:line2 - a:line1
silent execute (a:line1 + len(lines)) . ',' . a:line2 . 'd _'
endif
call setpos('.', cursor)
endfunction
command! -range=% Uniq call <SID>Uniq(<line1>, <line2>)
但这不一定比其他解决方案要好。
#6 楼
这是一个基于Preben Gulberg和Piet Delport的旧(2003)vim(golf)的解决方案。其根源在于
%g/^\v(.*)\n$/d
解决方案中,它已经被封装到一个函数中,因此它不会修改搜索寄存器,也不会修改未命名的寄存器。
为了简化用法,它也被封装到命令中:
:Uniq
(相当于:%Uniq
),:1,Uniq
(从缓冲区的开始到当前行),直观地选择行并点击
:Uniq<cr>
(通过vim扩展到:'<,'>Uniq
)等(
:h range
)代码是:
command! -range=% -nargs=0 Uniq <line1>,<line2>call s:EmuleUniq()
function! s:EmuleUniq() range
let l1 = a:firstline
let l2 = a:lastline
if l1 < l2
" Note the "-" to avoid spilling over the end of the range
" Note also the use of ":delete", along with the black hole register "_"
silent exe l1.','l2.'-g/^\(.*\)\n$/d _'
call histdel('search', -1) " necessary
" let @/ = histget('search', -1) " useless within a function
endif
endfunction
注意:他们的首次尝试是:
" Version1 from: Preben 'Peppe' Guldberg <peppe {at} xs4all {dot} nl>
" silent exe l1 . ',' . (l2 - 1) . 's/^\(.*\)\%(\n\%<' . (l2 + 1)
" \ . 'l$\)\+//e'
" Version from: Piet Delport <pjd {at} 303.za {dot} net>
" silent exe l1.','l2.'g/^\%<'.l2.'l\(.*\)\n$/d'
评论
不错但是,您的命令中需要一个$。否则,它将以与上一行相同的文本开头的行,但还有其他一些尾随字符的行来做意外的事情。还要注意,您给出的基本命令在功能上等效于我的回答:%!uniq,但是高亮和确认标志很好。
–通配符
2015年11月5日在21:12
没错,我刚刚检查了一下,如果重复的行之一包含不同的结尾字符,则该命令的行为与预期的不同。我不知道如何解决这个问题,原子\ n与行尾匹配,应该防止这种情况,但事实并非如此。我尝试在。*之后添加$,但没有成功。我将尝试修复它,但是如果无法解决,也许我会删除答案或在最后添加警告。感谢您指出这个问题。
– saginaw
2015年11月5日在21:37
尝试:%s / ^ \(。* \)\(\ n \ 1 \)\ + $ / \ 1 /
–通配符
2015年11月5日在21:58
您应该考虑$匹配字符串的末尾,而不是行尾。从技术上讲,这是不正确的,但是当您在字符后加上一些例外字符时,它会匹配文字$而不是任何特殊字符。因此,对于多行匹配,使用\ n更好。 (请参阅:help / $)
–通配符
2015年11月5日在22:05
我认为您是对的,\ n可以在正则表达式内的任何位置使用,而$应该只在结尾使用。为了使两者有所不同,我编辑了答案,写为\ n匹配换行符(本能地使您认为后面还有一些文本),而$匹配行尾(使您认为没有什么东西留下)。
– saginaw
2015年11月5日在22:24