如何在 Vim 中修剪文件末尾的空行?

发布于 2024-12-05 19:24:09 字数 329 浏览 0 评论 0原文

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

更新

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

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

autocmd BufWritePre *.py call TrimEndLines()

Sometimes I accidentally leave blank lines at the end of the file I am editing.
How can I trim them on saving in Vim?

Update

Thanks guys, all solutions seem to work.
Unfortunately, they all reset current cursor position, so I wrote the following function.

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

autocmd BufWritePre *.py call TrimEndLines()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

日暮斜阳 2024-12-12 19:24:09

这个替代命令应该做到这一点:

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

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

编辑

解释:

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

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

This substitute command should do it:

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

Note that this removes all trailing lines that contain only whitespace. To remove only truly "empty" lines, remove the \s* from the above command.

EDIT

Explanation:

  • \( ..... Start a match group
  • $\n ... Match a new line (end-of-line character followed by a carriage return).
  • \s* ... Allow any amount of whitespace on this new line
  • \) ..... End the match group
  • \+ ..... Allow any number of occurrences of this group (one or more).
  • \%$ ... Match the end of the file

Thus the regex matches any number of adjacent lines containing only whitespace, terminated only by the end of the file. The substitute command then replaces the match with a null string.

审判长 2024-12-12 19:24:09

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

:v/\_s*\S/d

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

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

:v/\n*./d

2. 在包含大块连续数据的巨大稀疏文件上
空白字符(从大约数百千字节开始)
空格)上述命令可能会产生不可接受的性能。
如果是这样的话,同样可以用优雅的想法来改造
:vglobal 命令进入速度更快(但可能更少
优雅的):delete 命令,具有模式定义的范围。

对于空行:

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

对于空行:

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

两个命令的本质是相同的;即删除行
属于指定范围,根据
分三步:

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

  2. 搜索描述非尾随空白的模式所在的行
    行(\_s*\S\n*.)不匹配(否定是由于
    \@! atom — 请参阅 :help /\@!)。将范围的起始线设置为
    该行。

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

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

1. An elegant solution can be based on the :vglobal command
(or, which is the same thing, on the :global with ! modifier):

:v/\_s*\S/d

This command executes :delete on every line that does not have
non-whitespace characters in it, as well as after it in the remaining
text to the end of buffer (see :help /\s, :help /\S, and :help /\_
to understand the pattern). Hence, the command removes the tailing
blank lines.

To delete the empty lines in a strict sense—as opposed to blank ones
containing only whitespace—change the pattern in that :vglobal
command as follows.

:v/\n*./d

2. On huge sparse files containing large blocks of consecutive
whitespace characters (starting from about hundreds of kilobytes of
whitespace) the above commands might have unacceptable performance.
If that is the case, the same elegant idea can be used to transform
that :vglobal commands into much faster (but perhaps less
elegantly-looking) :delete commands with pattern-defined ranges.

For blank lines:

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

For empty lines:

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

The essence of both commands is the same; namely, removing the lines
belonging to the specified ranges, which are defined according to the
following three steps:

  1. Move the cursor to the first line of the buffer before interpreting
    the rest of the range (0;—see :help :;). The difference between
    0 and 1 line numbers is that the former allows a match at the
    first line, when there is a search pattern used to define the ending
    line of the range.

  2. Search for a line where the pattern describing a non-tailing blank
    line (\_s*\S or \n*.) does not match (negation is due to the
    \@! atom—see :help /\@!). Set the starting line of the range to
    that line.

  3. Set the ending line of the range to the last line of the buffer
    (,$—see :help :$).

3. To run any of the above commands on saving, trigger it using
an autocommand to be fired on the BufWrite event (or its synonym,
BufWritePre).

零度℉ 2024-12-12 19:24:09

您可以将其放入 vimrc 中

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

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

详细信息:

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

You can put this into your vimrc

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

(replace *.txt with whatever globbing pattern you want)

Detail:

  • BufWritePre is the event before writing a buffer to a file.
  • $put _ appends a blank line at file end (from the always-empty register)
  • | chains Ex commands
  • $;?\(^\s*$\)\@!? goes to end of file ($) then (;) looks up backwards (?…?) for the first line which is not entirely blank (\(^\s*$\)\@!), also see :help /\@! for negative assertions in vim searches.
  • ×××+1,$ forms a range from line ×××+1 till the last line
  • d deletes the line range.
圈圈圆圆圈圈 2024-12-12 19:24:09

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

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

Inspired by solution from @Prince Goulash, add the following to your ~/.vimrc to remove trailing blank lines for every save for Ruby and Python files:

autocmd FileType ruby,python autocmd BufWritePre <buffer> :%s/\($\n\s*\)\+\%$//e
何时共饮酒 2024-12-12 19:24:09

我发现之前使用替代的答案在操作非常大的文件时会引起麻烦并污染我的寄存器。这是我想出的一个函数,它对我来说性能更好,并且避免污染寄存器:

" 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()

I found the previous answers using substitute caused trouble when operating on very large files and polluted my registers. Here's a function I came up with which performs better for me, and avoids polluting registers:

" 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()
耀眼的星火 2024-12-12 19:24:09

我有一个名为 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

这就是为什么我有一个单独的保留功能:

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()

I have a separated function called Preserve which I can call from other functions,
It makes easy to use Preserve to do other stuff:

" 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

In my case, I keep at least one blank line, but not more than one, at the end of the file.

" 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

Here's why I have a separated Preserve function:

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')

And if by any chance you want to create an autocommand you must create a group
to avoid overloading autocommands:

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

This one is to change file headers (if you have any suggestions) feel free to interact:

" 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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文