如何将前导空格转换为制表符?

5
许多人使用空格而不是制表符。我两者都使用。在行首使用制表符,在第一个非空白字符处使用空格。新建文档时没有问题,如果必须修改文档,则最好适应使用格式。但有时仍需要解决空格问题。
根据搜索和替换,我只需执行:%s / spaces_for_tab / tab / g 。这很简单,并且可以适用于许多情况。但是我只想重构行首的空格。

2
:set noet,选择要转换的块,:retab! - ninjalj
@ninjalj 是的,它可以工作,但有时选择要转换的块可能很困难。 - Martin Drlík
可能是如何在Vim或Linux中将空格转换为制表符?的重复问题。 - John Conde
4个回答

6

这更像是一个正则表达式问题。要锚定到行首,使用“caret”,例如:

s/^        /\t/

或者使用vim的内置功能实现:

:set tabstop=4  "four spaces will make up for one tab
:set noexpandtab  "tell vim to keep tabs instead of inserting spaces
:retab            "let vim handle your case

顺便说一下,我也更喜欢使用制表符进行缩进,空格用于对齐。不幸的是,vim 处理这个问题不太好(我不知道其他编辑器的情况),所以我主要使用 :set expandtab 命令(也许可以尝试使用 :set softtabstop 命令)。


通常情况下,您会想要使用 retab! 而不是 retab - Chris Morgan
1
问题是,正如你所写的那样,VIM 处理这个问题不太好(更可能是我错过了 VIM 的某些功能,因为 VIM 总是给我带来惊喜),因为 retab 会替换所有空格,而不仅仅是前导空格。 - Martin Drlík

2
我已经为此编写了一个简单的函数。无论如何,它只能适用于4个空格制表符。
fu! Fixspaces()
        while search('^\t* \{4}') != 0
                execute ':%s/^\t*\zs \{4}/\t/g'
        endwhile
endfu

如果有更好的解决方案,请提出来,我将很高兴使用它。 问题在于这个函数也会替换字符串中的空格。

这是我第一次标记我的答案。无论如何,这是一个很大的任务(防止更改多行字符串中的空格、HTML pre 标签等),我的实现有点 naive。 - Martin Drlík
这不是一个好的解决方案,而是使用vim的内置命令,如retabnoexpandtab结合使用。 - s g
如果我理解正确的话,马丁的投诉是retab命令单独使用时不能按预期工作,而retab!命令会将行内任意位置(即使是字符串字面值内部)的空格转换为制表符。实际上只有行首空白应该被转换为制表符才更合适。 - David Sanders

0

David的回答非常优雅,但它没有解决包含制表符和空格混合的前导空格的问题。例如,要将这样一行转换为:

<SPACE><SPACE><TAB>something...

你必须知道制表符的位置,以确定需要替换<TAB>并到达下一个制表位所需的空格数。我的解决方案虽然不像David的那样紧凑,但它解决了这个问题。它还允许我选择使用前导空格的方式,而不依赖于&expandtab。我会感激任何改进代码的建议...

function! TabsToSpaces(...)
  let ts = &tabstop
  let pos = getpos('.')
  while search('^ *\zs\t', "w") != 0
    let l:curpos = getcharpos('.')
    " The number of spaces needed depends upon the position of the <TAB>
    let numsp = 1 + ts - ( curpos[2] % ts )
    if numsp == 9
      let numsp = 1
    endif
    silent execute ':s/^ *\zs\t/'.repeat(' ', numsp).'/'
  endwhile
  if a:0 == 0
    echo 'Changed leading tabs to spaces'
  endif
  call setpos('.', pos)
endfunction

function! SpacesToTabs(...)
  let ts = &tabstop
  let pos = getpos('.')
  " First normalize all tabs to spaces
  call TabsToSpaces("quiet")
  while search('^\t* \{'.ts.'}') != 0
    silent execute ':%s/^\t*\zs \{'.ts.'}/\t/g'
  endwhile
  if a:0 == 0
    echo 'Changed leading spaces to tabs'
  endif
  call setpos('.', pos)                                                          
endfunction                                                                      

" Some keystrokes to implement the spaces/tabs functions
nmap <Leader>st :execute SpacesToTabs()<CR>
nmap <Leader>ts :execute TabsToSpaces()<CR>

-1

我借鉴了Martin的回答,并稍作改进,如果有人有兴趣:

function Fixspaces()
  let ts = &tabstop
  let pos = getpos('.')

  if &expandtab
    while search('^ *\t') != 0
      silent execute ':%s/^ *\zs\t/'.repeat(' ', ts).'/g'
    endwhile

    echo 'Changed tabs to spaces'
  else
    while search('^\t* \{'.ts.'}') != 0
      silent execute ':%s/^\t*\zs \{'.ts.'}/\t/g'
    endwhile

    echo 'Changed spaces to tabs'
  endif

  call setpos('.', pos)
endfunction

该函数根据expandtabtabstop的值执行适当操作,并记住光标的位置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接