我想在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
有没有一种不需要手动逐行复制粘贴的好方法来做这件事?
我想在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
有没有一种不需要手动逐行复制粘贴的好方法来做这件事?
你可以使用块模式选择进行一次性复制/粘贴,但我猜这不是你想要的。
如果你想仅使用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)/
del
前的 5,8
和 s///
前的 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)
按相反的顺序替换已删除的行。:sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohls
似乎更好,因为它在运行后清理搜索历史记录。而且它不会显示“x more/less lines”消息,需要我按回车键。 - ThiefMaster:help range
、:help :d
、:help :let
、:help split()
、:help :s
、:help :s\=
、:help remove()
。 - Benoit通过组合:global
、:move
和:join
命令,可以获得一个优雅而简洁的Ex命令来解决这个问题。假设第一块行从缓冲区的第一行开始,并且光标位于第二块的第一行之前的行上,则命令如下。
:1,g/^/''+m.|-j!
如果需要详细解释此技术,请参见我的回答,针对一个与之基本相同的问题“如何在Vim中实现“paste -d '␣'”行为?”。”。
1,
后,它可以正常运行而没有错误。 - ThiefMaster:move
和:join!
的用法,也不了解''
作为范围参数的含义(可以查看:help ''
),以及+
和-
作为范围修饰符的意思(可以查看:help range
)。谢谢! - rampion连接多行文本,需要按以下步骤进行:
jj
CTRL-v
$
CTRL-END
x
kk$
p
这种移动方式可能不是最好的(我不是专家),但它可以像您想要的那样工作。希望有更简短的版本。
以下是使此技术有效的先决条件:
abc
和def
)具有相同的长度XOR:%s/ \+/ /g
这样简单。 - Khaja Minhajuddin这是我会如何操作(光标在第一行):
qama:5<CR>y$'a$p:5<CR>dd'ajq3@a
你需要知道两个东西:
这是正在发生的事情:
qa
记录下一个 q
的所有内容到名为 a
的“缓冲区”中。ma
在当前行上创建一个标记。:5<CR>
跳转到下一组。y$
复制剩余的行。'a
返回之前设置的标记。$p
粘贴到该行的末尾。:5<CR>
返回第二组的第一行。dd
删除它。'a
返回之前的标记。jq
向下移一行,并停止录制。3@a
重复该行动作(在我的例子中为3次):5
后按下[Enter]
,否则这个操作将无法生效。 - Shawn J. Goff: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 键输入的。 - rampion如其他地方所述,块选择是最好的方法。但您还可以使用以下任何变体:
:!tail -n -6%|粘贴-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.
样本数据与rapion的相同。
:1,4s/$/\=getline(line('.')+4)/ | 5,8d
我认为不要把它弄得太复杂。
只需要打开虚拟编辑功能
(:set virtualedit=all
)
选择方块123及其下面的所有内容。
然后将其放置在第一列之后即可。
abc 123
def 45
... ...
并将两个以上的空格替换为一个空格:
:%s/\s\{2,}/ /g
gvV:'<,'>s/\s+//g
的事情(vim 应该会自动为您插入 '<,'>
,因此您不需要手动输入)。 - Benaaa
bbb
ccc
AAA
BBB
CCC
将光标置于第一行,按照以下步骤操作:
qa}jdd''pkJxjq
然后按下@a
(之后您可以随意使用@@
)直到需要次数。
最终应该得到:
aaaAAA
bbbBBB
cccCCC
(加上一个换行符。)
解释:
qa
开始在 a
中录制复杂的重复操作。
}
跳到下一个空行。
jdd
删除下一行。
''
回到最后一次跳转前的位置。
p
将删除的行粘贴在当前行下面。
kJ
将当前行添加到上一行的末尾。
x
删除 J
添加的组合行之间的空格;如果不需要空格,可以省略此步骤。
j
到下一行。
q
结束复杂的重复录制。
之后,您可以使用 @a
来运行存储在 a
中的复杂重复操作,然后可以使用 @@
重新运行上次运行的复杂重复操作。
有很多方法可以实现这一点。我将使用以下两种方法之一合并两个文本块。
假设第一个块在第1行,第二个块从第10行开始,并且光标的初始位置在第1行。
(\n表示按下回车键。)
1. abc
def
ghi
10. 123
456
789
使用移动第n行号和join命令的宏。qaqqa:+9y\npkJjq2@a10G3dd
qcqqc:10m .\nkJjq2@c
@rampion的回答给了我灵感,我制作了一个键位映射来合并两个块
" paste copied block(already stored in register) to the end of current area
function block_paste(direct)
let current_line = line('.')
let copied_contents = split(@", "\n")
for index in range(0, len(copied_contents) - 1)
if a:direct == 'head' " head
let res = copied_contents[index] . getline(current_line + index)
elseif a:direct == 'tail' " tail
let res = getline(current_line + index) . copied_contents[index]
else
let res = ''
endif
let target_line = current_line + index
if target_line <= line('$')
call setline(target_line, res)
else
call append(target_line - 1, res)
endif
endfor
endfunction
nnoremap <Leader>bp :call block_paste('tail')<CR>
nnoremap <Leader>bP :call block_paste('head')<CR>
按键映射 bp
可以将复制的块追加到当前块的末尾,而按键映射 bP
可以将复制的块插入到当前块的开头
如果您不喜欢 bp
/ bP
,可以绑定到任何您喜欢的按键映射
x
行与第x+2
行合并吗? - larsks[a[i] + b[i] for i in min(len(a), len(b))]
- ThiefMaster