首页 文章

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

提问于
浏览
321

我想在Vim中合并两个行块,即取行 n..m 并将它们附加到行 a..b . 如果您更喜欢伪代码解释: [a[i] + b[i] for i in min(len(a), len(b))]

例:

abc
def
...

123
45
...

应该成为

abc123
def45

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

9 回答

  • 55

    你可以用一次复制/粘贴(使用块模式选择)完成所有这些,但我猜这不是你想要的 .

    如果您只想使用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
    ~
    

    UPDATE: 这个许多赞成票的答案值得更彻底的解释 .

    在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
    ~
    

    我们需要指定一个不同的分隔符,以防止"work is"被分成两个列表元素: let l=split(@","\n") .

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

  • 1

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

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

    有关所用技术的详细说明,请参阅answer我给问题“Vim paste -d ' ' behavior out of the box?” .

  • 44

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

    • 转到第三行: jj

    • 进入可视区块模式: CTRL-v

    • 将光标锚定到行尾(对于不同长度的行很重要): $

    • 转到最后: CTRL-END

    • 剪切块: x

    • 转到第一行的末尾: kk$

    • 在此粘贴块: p

    这个运动不是最好的(我不是专家),但它的工作方式与你想要的一样 . 希望会有更短的版本 .

    以下是先决条件,因此这项技术运作良好:

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

    • 起始块的第一行是最长的,你不在乎中间的额外空格) XOR

    • 起始块的第一行不是最长的,并且末尾有额外的空格 .

  • 19

    这是我如何做到的(光标在第一行):

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

    你需要知道两件事:

    • 第二组第一行开始的行号(在我的例子中为5),和

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

    这是发生了什么:

    • qaq 中的所有内容记录到 a 中的"buffer"中 .

    • ma 在当前行上创建标记 .

    • :5<CR> 转到下一组 .

    • y$ 猛拉其余部分 .

    • 'a 返回到先前设置的标记 .

    • $p 贴在行尾 .

    • :5<CR> 返回第二组的第一行 .

    • dd 将其删除 .

    • 'a 返回标记 .

    • jq 向下一行,并停止录制 .

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

  • 2

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

    :!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.
    
  • 3

    样本数据与rampion相同 .

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

    我不认为让它变得太复杂 . 我只想设置virtualedit
    :set virtualedit=all
    选择块123以及下面的全部 .
    把它放在第一列之后:

    abc    123
    def    45
    ...    ...
    

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

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

    我会用复杂的重复:)

    鉴于这种:

    aaa
    bbb
    ccc
    
    AAA
    BBB
    CCC
    

    将光标放在第一行的第一个“a”处,按以下键:

    qq}jdd''$pkJj0q
    

    然后根据需要多次按 @q (您可以随后使用 @@ ) .

    你最终应该:

    aaaAAA
    bbbBBB
    cccCCC
    

    (另外一个新队 . )

  • 878

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

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

    (\ n表示按下回车键 . )

    1. abc
       def
       ghi        
    
    10. 123
        456
        789
    

    使用命令宏:复制,粘贴和连接 .

    qaqqa:9y \ npkJjq2 @ a10G3dd

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

    qcqqc:10m . \ nkJjq2 @ c

相关问题