首先,这是我在这里的第一篇文章,我想说的是,我发现VIM是一个很好的工具,并且这里的论坛对于寻找问题的答案非常有帮助,许多有用的人提供宝贵的帮助。我对VIM还是很陌生,所以我了解的几乎所有东西都来自这里。

我的问题是:我知道如何反转文件中的所有行(:g / ^ / m0等方式),但是有一种方法可以使文件中的每4行反转,从而使

成为


line1
line2
line3
line4
line5
line6
line7
line8
...


您可以假定此类文件中总是存在4行的精确倍数。

评论

关于“ p.s”:如果在一行的前面插入4个空格,它将被解释为代码(您可以选择文本并使用顶部的格式按钮)。您可以在顶部的询问图标上找到更多详细信息。

#1 楼

Vim Wiki上解释的命令:Reverse可以用于此目的(您可以将其包括在.vimrc中以使其永久化):每四行运行一次命令:

command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl


说明:




qm:正常情况下为“ q”模式开始(并停止)在给定寄存器(在这种情况下为寄存器'm',但可以是其他任何字母)中记录宏。

V:进入可视模式并选择当前行

3j:将可视选择范围扩展到下3行

:Reverse<cr>:在选定的行上运行“反向”命令​​(<cr>代表Enter键)

4j:转到下一个未更改的行

q:停止记录宏

1000@m:运行在寄存器“ m”处记录的宏1000次(您可以增加如果文件大于4000行,则使用此数字)


编辑:

在注释中提到,您可以使用递归宏而不是使用计数:

qmV3j:Reverse<cr>4jq
1000@m





qM:如果为q指定了寄存器如果是大写,则宏将附加到寄存器(当您意识到错过复杂宏的最后步骤时也很有用)。

@m:在寄存器m上运行宏


q:停止追加宏

尽管它是作为宏创建的,但是如果它是工作流程中的常见任务,则可以为此创建命令:

qmV3j:Reverse<cr>4jq
qM@mq
@m




function! Reverse4() / endfunction:定义新功能

let reg_m = @m:保存寄存器m的当前内容


let @m = "<c-r><c-r>m":将宏插入寄存器m中-请注意<c-r>是Ctrl + r的vim表示法,您应该键入它,而不是复制/粘贴,因此您的行将类似于let @m = 'V3j:Reverse^M4j@m',并且将包含特殊字符( ^ M)

normal! @m:正常命令在正常模式下输入时会运行其参数,因此它将运行递归宏。
let @m = reg_m:恢复寄存器m的内容,因此您不必记住该功能正在使用该寄存器,而不必使用它
command! Reverse4 call Reverse4():为此功能创建新命令

根据您的需要,您可以对其进行增强,例如:将参数传递给命令和函数,以便它适用于任意数量的行,而不是固定在4行的组中。

评论


除了使用1000 @ q hack外,您还可以使用递归宏:qmqqm ... @ mq @ m。

–门把手
16年1月29日在13:06

当我尝试运行“ qmV3j:Reverse 4jq”时,它说“ E492:不是编辑器命令”。我一定做错了什么。顺便说一句,我正在使用-并且仅使用过-GVIM。我应该首先提到这一点。

–ableablewasiereisawelba
16年1月29日在16:51

啊没关系。我知道了-我不应该在“ qmV3j:Reverse 4jq”部分之前键入“:”。有效! mMontu,非常感谢。如果我可能要问-我如何在我的.vimrc中包含“:Reverse”命令?

–ableablewasiereisawelba
16 Jan 29 '17:27



@ablewasiereisawelba要使该命令永久可用,只需编写line命令! -bar -range =%反转vimrc中某处的 g / ^ / m -1 | nohl。

– saginaw
16年1月29日在17:33



@saginaw哦,我没有意识到这很简单。谢谢。

–ableablewasiereisawelba
16年1月29日在17:39

#2 楼

与所有重复操作一样,如果您可以通过基本的编辑操作执行一次操作,那么可以通过记录宏轻松地执行多次操作。

(在这种情况下,我将使用递归宏,但是您可以录制一个非递归的音频,然后通过锤击@@或使用计数来多次播放。) br />

gg:移至文件的开头。

qqq:清除寄存器q。我们将在第5步中看到为什么这样做是必要的。通过手动删除和粘贴它们。 (乍看之下可能看起来很复杂,但是不会被欺骗。这是您在qq的前5分钟左右学到的非常基本的知识:ddjjpkddkkPjddpjjvimtutorjkdd

p:调用宏!此时,寄存器P不包含任何内容(因为我们已在步骤2中将其清除),因此将不会发生任何事情。

@q:停止记录宏。

q:重播递归宏根据需要进行多次。

NB如果文件包含的行数不能被四整除,则以上内容将无法产生预期的结果。为了在没有四行的情况下尽早停止宏,我们需要执行Vim正常模式命令,该命令将失败,然后在步骤4中开始应用编辑。我们可以通过尝试向下移动来完成此操作。在我们开始四处移动之前,先行三遍(然后备份):q

因此:

ggqqqqqddjjpkddkkPjddpjj@qq@q


评论


为什么要明确清除寄存器q?如果您仅记录到其中,无论如何都将被覆盖...。

–通配符
16 Jan 29 '14:34



@Wildcard因为它是一个递归宏,所以在您第一次调用寄存器q的内容时,它必须为空,否则会弄乱您的版本。

– saginaw
16年1月29日在14:39

非常好。我以交互方式尝试了此操作,而没有尝试键入命令的确切顺序,然后使用以下命令结束:

–通配符
16年1月29日在14:44

@ablewasiereisawelba,每次需要执行此操作时,我都可以在其中使用单行自定义命令-请注意,您不必每次都需要重新创建宏,因为可以存储它作为vimrc上的功能/命令。

–mMontu
16年2月1日在11:20

@ablewasiereisawelba我很高兴您发现“编辑”很有用!因为这是您的第一篇文章,也许您不知道StackExchange通知的工作原理:当您发表评论时,答案/问题的作者会收到通知;仅当您包含@ 时,其他人才会收到通知。因此,只有Rich收到有关您上一封邮件的通知。

–mMontu
16年2月12日在10:12

#3 楼

您还可以使用Ex命令(使用sed作为外部过滤器)来执行此操作:

:%!sed -n 'h;n;G;h;n;G;h;n;G;p'


此版本将忽略(删除)4的倍数以外的任何多余的行。保持最后一组少于4行(反转),请使用:

:%!sed -n '$p;h;n;G;$p;h;n;G;$p;h;n;G;p'


此处%的意思是“缓冲区中的每一行。”

!命令的意思是“以指定的行作为输入运行以下命令,并用命令的输出替换指定的行。” (这称为过滤器;对于诸如排序之类的事情非常方便,例如:%!sort将对文件中的所有行进行排序; :2,8!sort将对第2-8行进行排序,依此类推。)

sed是流编辑器工具,可以在所有类似Unix的系统上找到。此处使用的sed的关键概念是“模式空间”(默认情况下仅依次包含输入的每一行)和“保留空间”(您可以在其中使用sed保留额外的文本,同时保留额外的文本)处理其他输入行)。

-nsed命令的一个选项,用于抑制其打印模式空间的默认操作(因为在这种情况下,我们只希望在明确声明时打印)。

$p命令中的sed表示“如果在sed的输入的最后一行,则打印(图案空间)。”

h表示“保持当前位置“保持空间”中“模式空间”的内容,覆盖那里的所有内容。”

n的意思是“用输入的下一行替换'模式空间'的内容。”

G的意思是“附加到'模式空间':换行符,后跟'保持空间'的内容。”

总而言之,sed命令存储四行输出,在存储它们时将它们反转,然后打印它们。在第二个版本中添加的$p命令可确保,如果到达文件的最后一行而不是四行的倍数,则仍会打印这些行。


交互式方法仍不使用任何Vim特定功能,也不使用外部过滤器:

:4


转到第四行。

:.m -4 | +3m . | +2m . | +5


反转前四行(1-4),将光标留在第8行。

.m -4将当前行移到四行的后一行(将光标移到移动的行上。)

+3m .将当前行之后3行的行移动到当前行之后,将光标留在移动的行上。 +2m .当然工作原理相同。

+5将光标从其所在位置向下放置五行。

根据需要重复。

在Vim中,可以使用@:重复整个命令,然后再次使用@@重复整个命令。在POSIX viex中,您需要将:.m -4 | +3m . | +2m . | +5插入为一行文本,将其删除到命名缓冲区(寄存器),然后执行该命名缓冲区(寄存器)。

因此在ex模式下,仅使用POSIX指定的功能以交互方式反转行,并以17行文本开头:

Entering Ex mode.  Type "visual" to go to Normal mode.
:0a     # Append following text after "line 0" (i.e. insert at start of file).
.m -4 | +3m . | +2m . | +5
.       # End text insertion
:d k    # Delete that line to register k
line1   # This is a printout of the current line
:4      # Move to line 4
line4
:@k     # Execute register k to reverse lines 1-4
line8
:@@     # Execute register k again
line12
:@@     # Execute register k again
line16
:@@     # Execute register k again
line17
:%p     # Print the whole buffer (just to see what was done)
line4
line3
line2
line1
line8
line7
line6
line5
line12
line11
line10
line9
line16
line15
line14
line13
line17
:wq     # Save and quit



进一步阅读:


用于ex的POSIX规范
用于ex4310的POSIX规范
有实际用途吗?


评论


您能否再说明一下您的解决方案?

–vappolinario
16年1月29日在12:04

@vappolinario,我添加了一些进一步的解释。有帮助吗? :)

–通配符
16年1月29日在12:35

哇,答案很长。 :)实际上,我已经sed了,但是我只是将它与一个简单的脚本一起使用,该脚本在某个地方(可能在此板上)找到了,这是我遇到的一个问题的解决方案。但是,当我尝试运行建议的行时,它说“ sed”未被识别为内部或外部命令,可操作程序或批处理文件。我不确定是否需要在vim文件夹中进行sed操作。

–ableablewasiereisawelba
16年1月29日在16:58

@ablewasiereisawelba,如果您在Windows机器上运行且未使用Cygwin(或MobaXterm或类似产品),则可以尝试我提供的另一种方法:4G:.m -4 | + 3m。 | + 2m。 | +5 @:然后重复@@,直到文件被完全处理。不过,我建议安装MobaXterm。 :)

–通配符
16年1月29日在17:20

@Wildcard哦,好的,我现在正在检查MobaXterm。 :)

–ableablewasiereisawelba
16年1月29日在17:33

#4 楼

:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')


普通英语:对于每行,将当前行上移lnum % 4,除非lnum % 4 == 0除外,在这种情况下,将当前行上移4。行,将上述命令中的n替换为4

评论


非常好的单行命令。谢谢,djjcast。

–ableablewasiereisawelba
16年2月2日在17:37