在Vim中合并多行(两个块)


323

我想在Vim中合并两行线,即取线n..m并将其附加到lines a..b。如果您喜欢伪代码说明:[a[i] + b[i] for i in min(len(a), len(b))]

例:

abc
def
...

123
45
...

应该成为

abc123
def45

有没有一种好的方法,而无需手动进行复制和粘贴?


所以...您想加入交替行吗?也就是说,你想加入线x与加盟x+2
larsk's

1
不,我有两个单独的方块。伪代码:[a[i] + b[i] for i in min(len(a), len(b))]
ThiefMaster 2012年

Answers:


880

您当然可以通过单个复制/粘贴(使用块模式选择)来完成所有这些操作,但是我想那不是您想要的。

如果您只想使用Ex命令来执行此操作

:5,8del | let l=split(@") | 1,4s/$/\=remove(l,0)/

会转变

work it 
make it 
do it 
makes us 
harder
better
faster
stronger
~

进入

work it harder
make it better
do it faster
makes us stronger
~

更新:如此众多的答案应该得到更详尽的解释。

在Vim中,您可以使用竖线字符(|)链接多个Ex命令,因此上述内容等效于

:5,8del
:let l=split(@")
:1,4s/$/\=remove(l,0)/

许多Ex命令都接受一系列行作为前缀参数-在上述情况下,5,8before del和the 1,4before s///指定命令在其上进行操作的行。

del删除给定的行。它可以接受一个寄存器参数,但是当没有给出时,它会将行转储到未命名的寄存器中,@"就像在普通模式下删除一样。 let l=split(@")然后使用默认的定界符:空格将删除的行拆分为一个列表。要在删除的行中包含空格的输入上正常工作,例如:

more than 
hour 
our 
never 
ever
after
work is
over
~

我们需要指定其他定界符,以防止“工作成果”被分为两个列表元素:let l=split(@","\n")

最后,在替换中s/$/\=remove(l,0)/,我们用$expression的值替换每行()的结尾remove(l,0)remove(l,0)更改list l,删除并返回其第一个元素。这使我们可以按照读取顺序替换已删除的行。相反,我们可以使用来以相反的顺序替换已删除的行remove(l,-1)


1
嗯...我只需要按回车一次。而且不会在两半之间插入空格。如果行上有一些尾随空格(例如“ work it”示例中的空格),则该空格仍然存在。您可以使用s/\s*$/代替删除任何尾随空格s/$/
2012年

1
感谢您的解释。:sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohls似乎更好,因为它可以在运行后清除搜索历史记录。而且它没有显示“ x更多/更少的行”消息,要求我按Enter。
ThiefMaster 2012年

11
如果你想对这个问题的答案全VIM参考::help range:help :d:help :let:help split():help :s:help :s\=:help remove()
贝努瓦

16
确保像您这样的人想要发布这样的答案,这就是我成为主持人的原因。好秀:)
Tim Post

1
如果前四个句子后面没有空格,则会出现问题。
Reman 2013年

58

优雅而简洁的Ex命令解决的问题可以通过组合获得:global:move:join命令。假设第一个行块在缓冲区的第一行开始,并且光标位于第二个块的第一行之前的行上,则命令如下。

:1,g/^/''+m.|-j!

有关此技术的详细说明,请参见对类似问题“ Vim paste -d'的开箱即用行为?”。


E16: Invalid Range-但仍然可以。当删除1,它时,没有错误。
ThiefMaster 2012年

可惜的是您不能接受多个答案-否则您的答案也会带有绿色标记!
ThiefMaster 2012年

3
非常好!我不知道:move:join!,也没有什么''意思的范围参数(:help ''),什么+-意味着作为范围调节剂(:help range)。谢谢!
2012年

@ThiefMaster:该命令设计为在两条要连接的行范围彼此相邻时使用,以便光标最初位于第一个块的最后一行,紧接在第二个块的第一行之前。(请参见链接的答案和问题。)即使块中的行数不同,该命令也会按预期工作,尽管在这种情况下它会给出错误消息。可以通过sil!在命令前添加命令来抑制错误消息。
ib。

2
@ib .:我认为将详细的解释也放入此答案中也是一个好主意。
ThiefMaster 2012年

44

要连接线段,您必须执行以下步骤:

  1. 转到第三行: jj
  2. 进入可视块模式: CTRL-v
  3. 将光标锚定到行尾(对于不同长度的行很重要): $
  4. 转到最后: CTRL-END
  5. 切块: x
  6. 转到第一行的末尾: kk$
  7. 在此处粘贴块: p

这项运动不是最好的运动(我不是专家),但它的工作原理就像您想要的那样。希望会有一个简短的版本。

这是必备条件,因此此技术效果很好:

  • 起始块的所有行(在问题abc和中的示例中def)的长度XOR相同
  • 起始块的第一行最长,并且您不必关心它们之间的其他空格)XOR
  • 起始块的第一行不是最长的行,并且您在末尾增加了空格。

哇,真有趣!我从未想过它将以这种方式工作。
voithos,2012年

13
这仅因为abcdef长度相同而按需要进行。块选择将保留已删除文本的缩进,因此,如果在放置文本时光标位于短行上,则将这些行插入较长的字母之间;如果光标位于较长的字母上,则将空格插入到较短的字母之间一。
2012年

确实...有什么方法可以使用块复制和粘贴正确地做到这一点?毕竟,这是最简单的方法(尤其是如果由于某种原因而无法在此处查找更复杂的方法时)。
ThiefMaster 2012年

2
就像@Izkata所说的那样,您将遇到在较长的行之间插入文本的问题。要解决此问题,只需在第一行的末尾添加更多空格以使其最长,然后粘贴您的文本块即可。一旦完成,将多个空间压缩为一个:%s/ \+/ /g
就很

1
set ve=all应该帮助,请参阅vimdoc.sourceforge.net/htmldoc/options.html#'virtualedit
本·

19

这是我的操作方式(光标放在第一行):

qama:5<CR>y$'a$p:5<CR>dd'ajq3@a

您需要知道两件事:

  • 第二组的第一行开始的行号(在我的情况下为5),以及
  • 每组中的行数(在我的示例中为3)。

这是怎么回事:

  • qa将所有内容记录到下q一个内容中的“缓冲区”中a
  • ma 在当前行上创建一个标记。
  • :5<CR> 转到下一组。
  • y$ 将其余部分拉出。
  • 'a 返回到较早设置的标记。
  • $p 粘贴在该行的末尾。
  • :5<CR> 返回第二组的第一行。
  • dd 删除它。
  • 'a 返回标记。
  • jq 下降一行,并停止记录。
  • 3@a 对每行重复该操作(本例中为3)

1
两次键入[Enter]:5都必须按一次,否则将不起作用。
肖恩·高夫

1
我在gvim上。有什么方法可以在GVim中复制并粘贴该命令?即使我当前处于正常(?)模式,^ V也会自动以插入模式粘贴(这很有意义,这就是人们通常想要的)。我试过了,:norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@a但这似乎只能执行q
ThiefMaster 2012年

1
ThiefMaster:尝试一下:let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@a,在其中^M按CTRL-V然后按Enter键入字符。
2012年

8

如其他地方所述,块选择是必经之路。但是您也可以使用以下任何变体:

:!tail -n -6 % | paste -d '\0' % - | head -n 5

此方法依赖于UNIX命令行。该paste实用程序是为处理这种行合并而创建的。

PASTE(1)                  BSD General Commands Manual                 PASTE(1)

NAME
     paste -- merge corresponding or subsequent lines of files

SYNOPSIS
     paste [-s] [-d list] file ...

DESCRIPTION
     The paste utility concatenates the corresponding lines of the given input files, replacing all but the last file's newline characters with a single tab character,
     and writes the resulting lines to standard output.  If end-of-file is reached on an input file while other input files still contain data, the file is treated as if
     it were an endless source of empty lines.

使用块选择不是唯一的方法。它也不是最简单的。如我的答案paste -d所示,可以通过短Vim命令来实现所需的(类似)行为。
ib。

3
除此之外,我在Windows上,因此解决方案将涉及到与我的linux计算机建立SSH连接,然后将其从编辑器粘贴到终端,然后再粘贴回去。
ThiefMaster 2012年

3

样本数据与斜率相同。

:1,4s/$/\=getline(line('.')+4)/ | 5,8d

3

我认为不会太复杂。我只需在
:set virtualedit=all)上设置virtualedit,然后
选择下面的块123。
将其放在第一列之后:

abc    123
def    45
...    ...

并删除1个空格之间的多个空格:

:%s/\s\{2,}/ /g

这个问题实际上不要求空格,我会做类似的事情 gvV:'<,'>s/\s+//g(vim应该会自动'<,'>为您插入,因此您不需要手动键入它)。
2014年

2

我会使用复杂的重复:)

鉴于这种:

aaa
bbb
ccc

AAA
BBB
CCC

将光标放在第一行上,按以下命令:

qa}jdd''pkJxjq

然后根据需要多次按@a(您可能随后使用@@)。

您应该最终得到:

aaaAAA
bbbBBB
cccCCC

(加上换行符。)

说明:

  • qa 开始记录一个复杂的重复 a

  • } 跳到下一个空行

  • jdd 删除下一行

  • '' 返回上一跳之前的位置

  • p 将已删除的行粘贴到当前行下

  • kJ 将当前行附加到上一行的末尾

  • x删除在J合并的行之间添加的空间;如果需要空间,可以忽略此

  • j 转到下一行

  • q 结束复杂的重复录音

之后,您将用于@a运行存储在中的复杂重复a,然后可以用于@@重新运行上次运行的复杂重复。


1

可以有许多种方法来完成此任务。我将使用以下两种方法之一合并两个文本块。

假设第一个块在第1行,第二个块从第10行开始,光标的初始位置在第1行。

(\ n表示按Enter键。)

1. abc
   def
   ghi        

10. 123
    456
    789

使用宏使用以下命令:复制,粘贴和连接。

qaqqa:+ 9y \ npkJjq2 @ a10G3dd

与宏一起使用命令在第n个行号处移动一行并合并。

qcqqc:10m。\ nkJjq2 @ c

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.