ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Vim 中合并两个多行文本块?

vim

我想在 Vim 中合并两行代码,即取行  k 到  l 并将它们附加到行 m 到   ;n.如果您更喜欢伪代码解释:[line[k+i] + line[m+i] for i in range(min(l-k, n-m)+1)]

例如,

abc
def
...

123
45
...

应该成为

abc123
def45

有没有一种很好的方法可以做到这一点,而无需逐行手动复制和粘贴?

所以……你想加入交替行吗?也就是说,您想通过加入 x+2 加入行 x
不,我有两个单独的块。伪代码:[a[i] + b[i] for i in min(len(a), len(b))]
查看类似的问题(和答案!)here

r
rampion

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

如果您只想使用 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 命令接受一系列行作为前缀参数 - 在上述情况下,del 之前的 5,8s/// 之前的 1,4 指定命令操作的行。

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

more than 
hour 
our 
never 
ever
after
work is
over
~

我们需要指定不同的分隔符,以防止“工作是”被拆分为两个列表元素:let l=split(@","\n")

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


嗯...我只需要按回车键一次。它不会在两半之间插入空格。如果行上有一些尾随空格(如在“work it”示例中),那仍然存在。您可以使用 s/\s*$/ 而不是 s/$/ 删除任何尾随空格。
感谢您的解释。 :sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohls 似乎更好,因为它在运行后会清理搜索历史。而且它没有显示要求我按回车的“x more/less lines”消息。
如果您想要该答案的完整 vim 参考::help range:help :d:help :let:help split():help :s:help :s\=:help remove()
确保像你这样的人想发布这样的答案是我成为版主的原因。不错的演出 :)
如果前 4 句后没有空格,则会出现问题。
i
ib.

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

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

有关此技术的详细说明,请参阅my answer 对基本相同的问题“How to achieve the “paste -d '␣'” behavior out of the box in Vim?”。


E16: Invalid Range - 但它仍然有效。删除 1, 时,它可以正常工作而不会出现错误。
太糟糕了,你不能接受一个以上的答案——否则你的答案也会有一个绿色标记!
非常好!我不知道 :move:join!,也不知道 '' 作为范围参数 (:help '') 的含义以及 +- 作为范围修饰符 (:help range) 的含义。谢谢!
@ThiefMaster:该命令设计为在要连接的两个范围的行彼此相邻时使用,因此光标最初位于第一个块的最后一行,紧接在第二个块的第一行之前。 (请参阅链接的答案和问题。)即使块中的行数不同,该命令也会按预期工作,尽管在这种情况下会给出错误消息。可以通过在命令前添加 sil! 来抑制错误消息。
@ib .:我认为将详细解释也放入此答案中也是一个好主意。
m
mliebelt

要加入线块,您必须执行以下步骤:

转到第三行:jj 进入可视块模式:CTRL-v 将光标定位到行尾(对于不同长度的行很重要):$ 转到末尾:CTRL-END 剪切块:x 转到第一行的结尾:kk$ 在此处粘贴块:p

该运动不是最好的(我不是专家),但它可以像您想要的那样工作。希望会有一个更短的版本。

这是先决条件,因此此技术效果很好:

起始块的所有行(在问题 abc 和 def 的示例中)具有相同的长度 XOR

起始块的第一行是最长的,你不需要关心中间的额外空格)XOR

起始块的第一行不是最长的,并且您在末尾添加空格。


哇,这很有趣!我从来没有想过它会那样工作。
这样做只是因为 abcdef 的长度相同。块选择将保留已删除文本的缩进,因此,如果在放置文本时光标位于短行上,则行将插入较长的字母之间 - 如果光标位于较长的字母上,则将空格附加到较短的字母一。
确实......有没有办法使用块复制和粘贴正确地做到这一点?毕竟这是最简单的方法(特别是如果您由于某种原因无法在此处查找更复杂的方法)。
就像@Izkata 说的那样,您会遇到在较长的行之间插入文本的问题。要解决此问题,只需在第一行末尾添加更多空格以使其最长,然后粘贴您的文本块。完成后,将多个空格压缩为一个就像 :%s/ \+/ /g
set ve=all 应该有帮助,请参阅 vimdoc.sourceforge.net/htmldoc/options.html#'virtualedit'
T
ThiefMaster

这是我的做法(光标在第一行):

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

你需要知道两件事:

第二组的第一行开始的行号(在我的情况下为 5),以及

每组中的行数(在我的示例中为 3)。

这是发生了什么:

qa 将直到下一个 q 的所有内容记录到 a 中的“缓冲区”中。

ma 在当前行上创建一个标记。

:5 进入下一组。

y$ 抽出该行的其余部分。

'a 回到先前设定的标记。

$p 粘贴在行尾。

:5 返回到第二组的第一行。

dd 删除它。

'a 回到标记。

jq 下降一行,并停止记录。

3@a 为每一行重复该操作(在我的情况下为 3)


您必须在两次输入 :5 后按 [Enter],否则这将不起作用。
我在gvim上。有没有办法在 GVim 中复制和粘贴该命令? ^V 自动以插入模式粘贴(这是有道理的,这是人们通常想要的),即使我目前处于正常(?)模式。我尝试了 :norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@a,但似乎只执行了 q
ThiefMaster:试试 :let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@a,其中 ^M 字符是通过按 CTRL-V 然后 Enter 来输入的。
k
kevinlawler

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

:!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 命令实现,如 my answer 所示。
除此之外,我在 Windows 上,因此该解决方案将涉及打开与我的 linux 机器的 SSH 连接,并从编辑器粘贴到终端并返回。
k
kev

样本数据与 rampion 的相同。

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

R
Reman

我不认为让它太复杂。我只需将 virtualedit 设置为
(:set virtualedit=all)
选择块 123 及以下所有内容。
将其放在第一列之后:

abc    123
def    45
...    ...

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

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

这个问题实际上要求没有空格,我会做类似 gvV:'<,'>s/\s+//g 的事情(vim 应该自动为您插入 '<,'>,这样您就不需要手动输入它)。
G
Gerardo Marset

我会使用复杂的重复:)

鉴于这种:

aaa
bbb
ccc

AAA
BBB
CCC

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

qa}jdd''pkJxjq

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

你最终应该得到:

aaaAAA
bbbBBB
cccCCC

(加上换行符。)

解释:

qa 开始在 a 中记录复杂的重复

跳转到下一个空行

jdd 删除下一行

'' 回到上次跳转前的位置

将删除的行粘贴到当前行下

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

删除 J 在组合行之间添加的空间;如果你想要空间,你可以省略它

转到下一行

结束复杂的重复录音

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


d
dvk317960

可以有很多方法来实现这一点。我将使用以下两种方法中的任何一种来合并两个文本块。

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

(\n 表示按回车键。)

1. abc
   def
   ghi        

10. 123
    456
    789

使用以下命令使用宏:复制、粘贴和加入。

qaqqa:+9y\npkJjq2@a10G3dd

使用命令使用宏在第 n 行号处移动一行并加入。

qcqqc:10m .\nkJjq2@c