如何每4行反转一次?


13

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

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

line1
line2
line3
line4
line5
line6
line7
line8
...

变成

line4
line3
line2
line1
line8
line7
line6
line5
...

您可以假设在这些文件中总是存在4行的精确倍数。


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

Answers:


15

Vim Wiki上:Reverse解释的命令可用于此目的(您可以将其包括在其中以使其永久存在):.vimrc

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

然后,您可以记录一个宏以每四行运行一次命令:

qmV3j:Reverse<cr>4jq
1000@m

说明:

  • qm:正常模式下的“ q”开始(并停止)在给定寄存器(在这种情况下为寄存器“ m”,但可以是其他任何字母)中记录宏
  • V:进入可视模式并选择当前行
  • 3j:将视觉选择扩展到接下来的3行
  • :Reverse<cr>:在选定的行上运行“反向”命令​​(<cr>此处代表enter键)
  • 4j:转到下一个不变的行
  • q:停止录制宏
  • 1000@m:将在寄存器'm'处记录的宏运行1000次(如果文件大于4000行,则可以增加此数字)

编辑:

如评论中所述,您可以使用递归宏而不是使用count:

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM:如果指定的寄存器q是大写的,则宏将附加到寄存器(当您意识到错过复杂宏的最后步骤时,这也很有用)
  • @m:在寄存器上运行宏 m
  • q:停止追加宏

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

function! Reverse4()
   let reg_m = @m
   let @m = '<c-r><c-r>m'
   normal! @m
   let @m = reg_m
endfunction
command! Reverse4 call Reverse4()
  • function! Reverse4()/ endfunction:定义一个新函数
  • let reg_m = @m:保存寄存器的当前内容 m
  • let @m = "<c-r><c-r>m":在寄存器中插入宏m-请注意,这<c-r>Ctrl+ 的vim标记r,您应该键入字符,而不是复制/粘贴,因此您的行将类似于let @m = 'V3j:Reverse^M4j@m'并且将包含特殊字符(^ M)
  • normal! @m:普通命令会像在普通模式下键入命令一样运行其参数,因此它将运行递归宏
  • let @m = reg_m:恢复寄存器的内容m,因此您不必记住该功能正在使用该寄存器,并且避免使用它

  • command! Reverse4 call Reverse4():为此功能创建一个新命令

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


除了使用1000@qhack之外,您还可以使用递归宏:qmqqm ... @mq@m
Doorknob '16

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

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

3
@ablewasiereisawelba要使该命令永久可用,只需command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl在vimrc中的某处编写该行。
saginaw '16

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

9

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

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

ggqqqqqddjjpkddkkPjddpjj@qq@q

分解

  1. gg:移至文件的开头。
  2. qqq:清除注册q。我们将在步骤5中了解为什么需要这样做。
  3. qq:开始录制宏以注册q
  4. ddjjpkddkkPjddpjj:一系列操作,只需手动删除和粘贴即可对前四行进行重新排序。(这可能第一眼看起来很复杂,但不要被欺骗,这是非常基本的东西,你会在第一个5分钟学到左右。vimtutorjkddpP
  5. @q:调用宏!此时,寄存器不q包含任何内容(因为我们在步骤2中将其清除了),所以什么也不会发生。
  6. q:停止录制宏。
  7. @q:根据需要重播递归宏多次。

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

从而:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q

为什么要明确清除寄存器q?如果您仅记录在其中,那里的内容将被覆盖。...–
通配符

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

非常好。我以交互方式尝试了此操作,而没有尝试键入命令的确切顺序,并且使用了以下方法:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
通配符

1
@ablewasiereisawelba- where I can just use the one-line custom command each time I need to do this注意您不必每次都需要重新创建宏,因为可以将其作为函数/命令存储在vimrc上。
mMontu'2

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

7

您也可以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命令的一个选项,以禁止其打印模式空间的默认操作(因为在这种情况下,我们仅在明确声明时才要打印。)

$psed命令中的意思是“如果您在sed输入的最后一行,请打印(图案空间)。”

h 表示“将'模式空间'的当前内容粘贴到'保持空间'中,覆盖其中的所有内容。”

n 表示“用输入的下一行替换'pattern space'的内容”。

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

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


对于另一种交互式方法,仍然不使用任何特定于Vim的功能,也不使用外部过滤器:

:4

转到第四行。

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

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

.m -4移动当前行到刚刚经过线路四行背面(离开光标上移动的线)。

+3m .移动即3线的线条的电流线,只是当前行之后,留下光标上移动的线。 +2m .当然是一样的。

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

根据需要重复。

在Vim中,您可以使用重复整个命令@:,然后使用再次重复@@。在POSIX中,vi或者ex您需要插入 :.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

进一步阅读:


您能否再解释一下您的解决方案?
vappolinario '16

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

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

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

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

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

普通英语:对于每行,将当前行向上移动lnum % 4,除非lnum % 4 == 0,在这种情况下,将当前行向上移动4

另外,要反转每一n行,请用替换4上述命令中的n


2
非常好的单行命令。谢谢,djjcast。
ablewasiereisawelba
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.