如何在 Vim 或 Linux 中将空格转换为制表符?

Posted

技术标签:

【中文标题】如何在 Vim 或 Linux 中将空格转换为制表符?【英文标题】:How can I convert spaces to tabs in Vim or Linux? 【发布时间】:2012-02-24 15:46:48 【问题描述】:

我查看了 Stack Overflow 上的几个问题,了解如何将空格转换为制表符而不找到我需要的内容。似乎有更多关于如何将制表符转换为空格的问题,但我正在尝试做相反的事情。

Vim 中,我尝试过:retab:retab!,但没有运气,但我相信这些实际上是为了从制表符到空格。

我在命令提示符下尝试了expandunexpand,但没有任何运气。

这是有问题的文件:

http://gdata-python-client.googlecode.com/hg-history/a9ed9edefd61a0ba0e18c43e448472051821003a/samples/docs/docs_v3_example.py

如何使用 Vim 或 shell 将 前导 空格转换为制表符?

【问题讨论】:

在@Matt 的现在已删除的评论中,第一个示例 (sed "s/ +/`echo -e '\t'`/g" < input.py > output.py) 似乎转换了所有空格,而不仅仅是前导空格。在第二个示例 (sed "s/^ +/`echo -e '\t'`/g" < input.py > output.py ) 中,它仅将每行的第一个空格替换为一个制表符,并保留其余的空格。 对面相关:How to replace tabs with spaces? at Vim SE 我没有找到所有/许多文件的答案,所以我自己写了一个:***.com/a/44581474/1115187。使用findawk 和二十一点(不过,太长了,无法将其留在 cmets 中) 这能回答你的问题吗? How can I convert tabs to spaces in every file of a directory? 【参考方案1】:

在带有 GNU coreutils 的 shell 中: unexpand -t2 --first-only originalFile > newFile 解释: unexpand: 将空格变成制表符的命令 -t2:将 2 个空格变成 1 个制表符 --first-only:只修改行首的空格(包括混合制表符和空格),不修改行中或行尾的空格

它的倒数是expand,它需要一个简单的-i 而不是--first-only-t2 不变

Johnsyweb 流行的绿色检查答案完全是错误的,因为它修改了所有地方的空格!!!

附言这篇文章的疯狂格式并不能反映内容的质量。它反映了 *** 如何不支持我的浏览器。谢谢。

【讨论】:

【参考方案2】:

将所有空格更改为制表符 :%s/\s/\t/g

【讨论】:

这正是我所需要的。谢谢。我必须做的唯一改变是用制表符替换每 4 个空格,而不是每一个空格。 这会将所有空格转换为制表符,而不仅仅是缩进。【参考方案3】:

要使用 Vim 将一组文件(例如目录层次结构中的所有 *.ts 文件)从 2 个空格变为 4 个空格,您可以从命令行尝试:

find . -name '*.ts' -print0 | xargs -0 -n1 vim -e '+set ts=2 noet | retab! | set ts=4 et | retab | wq'

这样做是使用find 将所有匹配的文件传递给xargs(find 上的 -print0 选项与 xargs 的 -0 选项一起使用,以便处理名称中带有空格的文件)。

xargs 在每个文件上以 ex 模式 (-e) 运行 vim,执行给定的 ex 命令实际上是几个命令,将现有的前导空格更改为制表符,重置制表位并将制表符更改回空格,最后保存并退出。

在 ex 模式下运行可以防止这种情况:Vim: Warning: Input is not from a terminal 用于每个文件。

【讨论】:

【参考方案4】:

Linux:使用unexpand(和expand

这是一个非常好的解决方案:https://***.com/a/11094620/1115187,主要是因为它使用了 *nix-utilities:

    unexpand — 空格 -> 制表符 expand — 制表符 -> 空格

Linux:自定义脚本

我原来的答案

Bash sn-p 用于在 ./app 文件夹中的所有 .py 文件中用制表符替换 4-空格缩进(脚本中有两个 4)(递归):

find ./app -iname '*.py' -type f \
    -exec awk -i inplace \
    ' match($0, /^(( 4)*)(.*?)$/, arr); gsub(/ 4/, "\t", arr[1]) ;  print arr[1] arr[3] '  \; 

它不会修改中间或末尾的 4 个空格。

在 Ubuntu 16.0x 和 Linux Mint 18 下测试

【讨论】:

【参考方案5】:

如果您安装了 GNU coreutils,请考虑 %!unexpand --first-only 或对于 4 空格制表符,请考虑 %!unexpand -t 4 --first-only--first-only 存在,以防您不小心将 unexpand--all 一起调用)。

请注意,这只会替换规定制表位之前的空格,而不是它们后面的空格;除非您更字面地显示选项卡,否则您不会在 vim 中看到任何视觉差异;例如,我的~/.vimrc 包含set list listchars=tab:▸┈(我怀疑这就是你认为unexpand 不起作用的原因)。

【讨论】:

【参考方案6】:

就我而言,我有多个空格(字段由一个或多个空格分隔),我想用制表符替换它们。以下是这样做的:

:% s/\s\+/\t/g

【讨论】:

【参考方案7】:
:%s/\(^\s*\)\@<=    /\t/g

翻译:搜索 4 个连续空格的每个实例(在 = 字符之后),但前提是直到该点的整行都是空格(这使用零宽度后视断言,\@&lt;=)。用制表符替换每个找到的实例。

【讨论】:

1个空格扩展到1个标签的唯一解决方案,但要注意答案中的unicode,我只是使用:%s/\(^\s*\)\@&lt;= /\t/g -在&lt;= 之后放置适当数量的空格(将 4 个空格转换为 1 个制表符,放入 4 个空格)【参考方案8】:

简单的 Python 脚本:

import os

SOURCE_ROOT = "ROOT DIRECTORY - WILL CONVERT ALL UNDERNEATH"

for root, dirs, files in os.walk(SOURCE_ROOT):
    for f in files:
        fpath = os.path.join(root,f)
        assert os.path.exists(fpath)
        data = open(fpath, "r").read()
        data = data.replace("    ", "\t")
        outfile = open(fpath, "w")
        outfile.write(data)
        outfile.close()

【讨论】:

一个大问题:这个 sn-p 替换了 all 4 个空格,不仅仅是缩进(在行首) 第二个:文件过滤器没有实现【参考方案9】:

使用 Vim 扩展所有 前导 空格(比 'tabstop' 更宽),你使用 retab 是正确的,但首先要确保 'expandtab' 被重置(:verbose set ts? et? 是你的朋友)。 retab 有一个范围,所以我通常指定% 表示“整个文件”。

:set tabstop=2      " To match the sample file
:set noexpandtab    " Use tabs, not spaces
:%retab!            " Retabulate the whole file

在做这样的事情之前(尤其是对 Python 文件!),我通常设置'list',以便我可以看到空格并进行更改。

为此,我在my .vimrc 中有以下映射:

nnoremap    <F2> :<C-U>setlocal lcs=tab:>-,trail:-,eol:$ list! list? <CR>

【讨论】:

这是我如何让它工作的,不知道什么是必要的,什么不是,顺便说一句,我不知道 reatab 之前的“%”在做什么::set noet,:set tabstop=2:retab!:%retab!:set tabstop=1:retab!:%retab! 我已经更新了我的答案,包括解释我为什么使用%。 The F2 mapping is exactly as it's typed. 对于那个特定的文件,我会这样做::set noet ts=2 |%retab! @Johnsyweb 公平地说,我错了::%retab! 仍然有效。我对==确实 尊重preserveindent 设置感到困惑。 要将咖啡脚本文件从空格转换为制表符,我遵循@Johnsyweb 的回答(包括添加到 .vimrc 文件中),而是使用:set tabstop=4【参考方案10】:

1 - 如果您有空格并需要制表符。

首先,您需要确定一个制表符有多少个空格。也就是说,假设您有前导 4 个空格或 8 个空格的行...比您意识到您可能希望制表符为 4 个空格。现在有了这些信息,您可以:

:set ts=4
:set noet
:%retab!

这里有问题!这一系列命令将查找您的所有文本,而不仅仅是行首的空格。这意味着像"Hey,␣this␣␣␣␣is␣4␣spaces" 这样的字符串将变为"Hey,␣this⇥is␣4␣spaces",但不是!它是一个标签!。

为了解决这个小问题,我推荐search,而不是retab

:%s/^\(^I*\)␣␣␣␣/\1^I/g

此搜索将在整个文件中查找以任意数量的制表符开头的任何行,后跟 4 个空格,并将其替换为找到的任何数量的制表符加一。

很遗憾,这不会立即运行!

首先,文件会有以空格开头的行。然后搜索将仅将前 4 个空格转换为制表符,并让以下...

您需要重复该命令。多少次?直到您收到pattern not found。我还想不出一种方法来自动化这个过程。但如果你这样做:

`10@:`

你可能已经完成了。此命令重复最后一次搜索/替换 10 次。您的程序不太可能有这么多缩进。如果有,请再次重复@@

现在,只是为了完成答案。我知道您要求相反,但您永远不知道何时需要撤消操作。

2 - 你有制表符并且想要空格。

首先,确定您希望将制表符转换为多少个空格。假设您希望每个选项卡为 2 个空格。然后你做:

:set ts=2
:set et
:%retab!

这对字符串也会有同样的问题。但由于它更好的编程风格是不在字符串中使用硬制表符,所以你实际上在这里做了一件好事。如果您确实需要在字符串中使用制表符,请使用\t

【讨论】:

似乎是一种极为罕见的竞争状况。无论如何,创建一个接受视觉范围选择的函数并迭代搜索函数,直到找不到匹配项,我想这将是一个更聪明和有用的答案。 是的,或者那样。如果你想要一个更明确的解决方案,你可以使用一个函数。无论如何,知道如何在ex command 中操作总是好的,因为这就是函数内部的内容。不,它并不罕见。你只需要有带空格的字符串就可以了。一点也不罕见。我一直在那里。感谢您的评论。 请解释为什么 /g 不足以将每行连续空格中的所有 4 组转换为制表符,而不必多次运行搜索。 嗨@BjarturThorlacius,问题是如果您删除^ 符号,从行首开始搜索,您可能会更改字符串中的空格。使用^,您可以保证从行首开始只更改空格和制表符,因此,缩进。除此之外,如果您确定可以一次完成所有操作,请删除 ^ 并只运行一次::%s/\(^I*\)␣␣␣␣/\1^I/g

以上是关于如何在 Vim 或 Linux 中将空格转换为制表符?的主要内容,如果未能解决你的问题,请参考以下文章

如何在现有文件中将制表符转换为空格,反之亦然

在 Notepad++ 中将制表符转换为空格

在 TextWrangler 中自动将制表符转换为 4 个空格?

如何让linux中的空格和制表符原形毕露

将多个文件中的空格转换为制表符 Sublime Text 2

如何将制表符转换为空格并在android studio中保存时自动格式化