【问题标题】:How can I trim blank lines at the end of file in Vim?如何在 Vim 中修剪文件末尾的空白行?
【发布时间】:2011-11-21 16:41:01
【问题描述】:

有时我不小心在我正在编辑的文件末尾留下了空白行。
在 Vim 中保存时如何修剪它们?

更新

谢谢大家,所有解决方案似乎都有效。
不幸的是,它们都重置了当前光标位置,所以我编写了以下函数。

function TrimEndLines()
    let save_cursor = getpos(".")
    silent! %s#\($\n\s*\)\+\%$##
    call setpos('.', save_cursor)
endfunction

autocmd BufWritePre *.py call TrimEndLines()

【问题讨论】:

  • 如果保留光标位置对您很重要,您可以在问题中指定该要求。
  • 对不起,我认为它不能重置光标位置。

标签: vim


【解决方案1】:

我有一个名为 Preserve 的单独函数,我可以从其他函数中调用它, 它使使用 Preserve 做其他事情变得容易:

" remove consecutive blank lines
" see Preserve function definition
" another way to remove blank lines :g/^$/,/./-j
" Reference: https://stackoverflow.com/a/7496112/2571881
if !exists('*DelBlankLines')
    fun! DelBlankLines() range
        if !&binary && &filetype != 'diff'
            call Preserve(':%s/\s\+$//e')
            call Preserve(':%s/^\n\{2,}/\r/ge')
            call Preserve(':%s/\v($\n\s*)+%$/\r/e')
        endif
    endfun
endif

就我而言,我在文件末尾至少保留一个空行,但不超过一个。

" Utility function that save last search and cursor position
" http://technotales.wordpress.com/2010/03/31/preserve-a-vim-function-that-keeps-your-state/
" video from vimcasts.org: http://vimcasts.org/episodes/tidying-whitespace
" using 'execute' command doesn't overwrite the last search pattern, so I
" don't need to store and restore it.
" preserve function
if !exists('*Preserve')
    function! Preserve(command)
        try
            let l:win_view = winsaveview()
            "silent! keepjumps keeppatterns execute a:command
            silent! execute 'keeppatterns keepjumps ' . a:command
        finally
            call winrestview(l:win_view)
        endtry
    endfunction
endif

这就是为什么我有一个单独的 Preserve 函数:

command! -nargs=0 Reindent :call Preserve('exec "normal! gg=G"')

" join lines keeping cursor position
nnoremap J :call Preserve(':join')<CR>
nnoremap <Leader>J :call Preserve(':join!')<CR>

" Reloads vimrc after saving but keep cursor position
if !exists('*ReloadVimrcFunction')
    function! ReloadVimrcFunction()
        call Preserve(':source $MYVIMRC')
        " hi Normal guibg=NONE ctermbg=NONE
        windo redraw
        echom "Reloaded init.vim"
    endfunction
endif
noremap <silent> <Leader>v :drop $MYVIMRC<cr>
command! -nargs=0 ReloadVimrc :call ReloadVimrcFunction()

" Strip trailing whitespaces
command! Cls :call Preserve(':%s/\v\s+$//e')

如果你想创建一个自动命令,你必须创建一个组 避免重载自动命令:

augroup removetrailingspaces
    au!
    au! BufwritePre *.md,*.py,*.sh,*.zsh,*.txt :call Preserve(':%s/\v\s+$//e')
augroup END

这个是改文件头(如果有什么建议)欢迎交流:

" trying avoid searching history in this function
if !exists('*ChangeHeader')
    fun! ChangeHeader() abort
        if line('$')>=7
            call Preserve(':1,7s/\v(Last (Change|Modified)|date):\s+\zs.*/\=strftime("%b %d, %Y - %H:%M")/ei')
        endif
    endfun
endif

" dos2unix ^M
if !exists('*Dos2unixFunction')
    fun! Dos2unixFunction() abort
        "call Preserve('%s/ $//ge')
        call Preserve(":%s/\x0D$//e")
        set ff=unix
        set bomb
        set encoding=utf-8
        set fileencoding=utf-8
    endfun
endif
com! Dos2Unix :call Dos2unixFunction()

【讨论】:

    【解决方案2】:

    1. 一个优雅的解决方案可以基于:vglobal 命令 (或者,在 :global 和 ! 修饰符上也是一样的):

    :v/\_s*\S/d
    

    此命令在没有的每一行上执行:delete 其中的非空白字符,以及在它之后的其余字符 缓冲区末尾的文本(请参阅:help /\s、:help /\S 和 :help /\_ 了解模式)。因此,该命令删除了拖尾 空白行。

    严格意义上的删除 empty 行——而不是空白行 仅包含空格 - 更改 :vglobal 中的模式 命令如下。

    :v/\n*./d
    

    2. 对于包含大块连续的大型稀疏文件 空白字符(从大约数百千字节的 whitespace) 上述命令可能具有不可接受的性能。 如果是这样的话,同样的优雅的想法可以用来改造 :vglobal 命令的速度要快得多(但可能更少 外观优雅):delete 具有模式定义范围的命令。

    对于空行:

    :0;/^\%(\_s*\S\)\@!/,$d
    

    对于空行:

    :0;/^\%(\n*.\)\@!/,$d
    

    两个命令的本质是一样的;即,删除行 属于指定范围,这些范围是根据 以下三个步骤:

    1. 在解释之前将光标移动到缓冲区的第一行 范围的其余部分(0;—参见:help :;)。和...之间的不同 0 和 1 行号是前者允许在 第一行,当有用于定义结尾的搜索模式时 范围的行。

    2. 搜索描述非拖尾空白的模式的行 行(\_s*\S 或 \n*.)不匹配(否定是由于 \@!atom——见:help /\@!)。将范围的起始行设置为 那条线。

    3. 将范围的结束行设置为缓冲区的最后一行 (,$——见:help :$)。

    3. 要在保存时运行上述任何命令,请使用以下命令触发它 在BufWrite 事件(或其同义词, BufWritePre)。

    【讨论】:

    • 这不仅更优雅,而且更实用。人们有时会混淆掌握某事的价值,这不是通过过度设计您有限的知识子集,而是从您的工具能够提供的功能中学习新的可能性。
    • 我最初的想法是在:d 中使用一些范围,例如$?\s*.?+d,但我发现这个解决方案要好得多。遗憾的是,我必须承认,我通常不会想到多行正则表达式以及 :g 或 :v。谢谢分享。
    • +1 表示解决方案的优雅。但是我怀疑如果有大量的空格,这个命令的效率可能会降低。
    • @Benoit:您的怀疑是正确的:从大约数百 KB 的连续空白开始,:vglobals 变得更慢。但是,用于检测拖尾空白行或空行的相同模式可用于查找从该位置开始到文件末尾要删除的第一行。 (请参阅更新后的答案。)顺便说一下,在几乎全空白文件的情况下,直接替换解决方案通常无济于事(它因E363 错误而失败),因为它需要大量内存来进行模式匹配(通常大于maxmempattern 选项的值)。
    • 不错的答案!你能对最后两个命令做一些解释吗?
    【解决方案3】:

    我发现以前使用替代的答案在处理非常大的文件时会造成麻烦并污染我的寄存器。这是我想出的一个功能,它对我来说性能更好,并且避免污染寄存器:

    " Strip trailing empty newlines
    function TrimTrailingLines()
      let lastLine = line('$')
      let lastNonblankLine = prevnonblank(lastLine)
      if lastLine > 0 && lastNonblankLine != lastLine
        silent! execute lastNonblankLine + 1 . ',$delete _'
      endif
    endfunction
    
    autocmd BufWritePre <buffer> call TrimTrailingLines()
    

    【讨论】:

      【解决方案4】:

      受@Prince Goulash 解决方案的启发,将以下内容添加到您的~/.vimrc 以删除每次保存 Ruby 和 Python 文件的尾随空行:

      autocmd FileType ruby,python autocmd BufWritePre <buffer> :%s/\($\n\s*\)\+\%$//e
      

      【讨论】:

        【解决方案5】:

        这个替代命令应该这样做:

        :%s#\($\n\s*\)\+\%$##
        

        请注意,这会删除所有仅包含空格的尾随行。要仅删除真正的“空”行,请从上述命令中删除 \s*。

        编辑

        解释:

        • \( ..... 开始一个匹配组
        • $\n ... 匹配新行(行尾字符后跟回车)。
        • \s* ... 在这一新行上允许任意数量的空格
        • \) .....结束比赛组
        • \+ ..... 允许该组出现任意次数(一次或多次)。
        • \%$ ... 匹配文件末尾

        因此,正则表达式匹配任意数量的仅包含空格的相邻行,仅在文件末尾终止。然后,替换命令将匹配项替换为空字符串。

        【讨论】:

        • 不错的一个!你能为我们分解一下吗?特别是它在文件末尾的匹配方式...
        • @Merlyn Morgan-Graham:# 是模式分隔符。您可以在使用s 命令的地方选择它,/ 不是强制性的。
        • @Benoit:谢谢 :) 我盯着它看了一分钟后就明白了。继续遗忘。现在我试图弄清楚这与文件末尾的匹配方式。
        • 另一种模式是/\_s*\%$
        • @Benoit:当心这种模式,因为它会导致删除最后一个非空行上的尾随空格,这可能是不可取的。
        【解决方案6】:

        你可以把它放到你的 vimrc 中

        au BufWritePre *.txt $put _ | $;?\(^\s*$\)\@!?+1,$d
        

        (将*.txt 替换为您想要的任何通配模式)

        详情:

        • BufWritePre 是将缓冲区写入文件之前的事件。
        • $put _ 在文件末尾附加一个空行(来自始终为空的寄存器)
        • | 连锁 Ex 命令
        • $;?\(^\s*$\)\@!? 转到文件末尾 ($) 然后 (;) 向后查找 (?…?) 以查找并非完全空白的第一行 (\(^\s*$\)\@!),另请参阅 :help /\@! 以获取否定断言在 vim 搜索中。
        • ×××+1,$ 形成从第 ×××+1 行到最后一行的范围
        • d 删除行范围。

        【讨论】:

        • +1;我喜欢使用$put _ 来确保始终有一个空行。
        • 这是一个原始而复杂的命令!不过,它有一个流程。当缓冲区的所有行都不为空时(例如,考虑几行仅包含一个空格字符),此命令不会删除任何这些行!顺便说一句,基于:global 命令,可以使用类似的想法构造一个更简洁的 Ex 命令。
        • @ib.: 一个只包含空格的文件在保存之前会被用户注意到(奇怪的是它是一个使用 Whitespace 语言的程序!)
        • @Benoit:可以注意到。或者不能。自动化的全部意义在于摆脱对需要运行命令的特定情况的监视。尽管如此,如果文件中没有非空行,建议的命令不会删除文件末尾的空行。因此,此命令并不能完全解决问题(问题的措辞对于这种情况也不例外)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-16
        • 2013-09-11
        • 1970-01-01
        相关资源
        最近更新 更多