如何在Vim中将文件中行和列的内容转置?

14

我知道可以使用Awk,但是我在Windows系统上,并且我正在创建一个函数给那些可能没有Awk的人使用。我也知道可以写C程序,但是对于我正在制作的一个小型Vim工具而言,我希望不需要编译和维护。

原始文件可能是:

THE DAY WAS LONG 
THE WAY WAS FAST

转置后应变成:

TT
HH
EE

DW
AA
YY

WW
AA
SS

LF
OA
NS
GT

更新


2
字符数最少...类似于得分最低赢的东西。 - ojblass
如果该解决方案只转置所选文本,那就更好了……但是,好吧,当前的解决方案对我来说已经足够好了。 :) - Denilson Sá Maia
下面是我在 这里 给出的脚本,从“高尔夫”意义上来说,似乎是目前已发布的 Vim-only 解决方案中最短的,这也是你作为选择正确答案的决定因素之一,不是吗? - ib.
6个回答

12

这是一条 Vim 语言的命令,因此您不需要编译带有 +python 支持的 Vim。

function! s:transpose()
    let maxcol = 0
    let lines = getline(1, line('$'))

    for line in lines
        let len = len(line)
        if len > maxcol 
            let maxcol = len
        endif
    endfor

    let newlines = []
    for col in range(0, maxcol - 1)
        let newline = ''
        for line in lines
            let line_with_extra_spaces = printf('%-'.maxcol.'s', line)
            let newline .= line_with_extra_spaces[col]
        endfor
        call add(newlines, newline)
    endfor

    1,$"_d
    call setline(1, newlines)
endfunction

command! TransposeBuffer call s:transpose()

将以下内容放入vim/plugin目录下新创建的.vim文件中,或者放入[._]vimrc文件中。
执行:TransposeBuffer命令以转置当前缓冲区。

注:请将[._]替换为点号或下划线。

纯粹的 Vim 让你获得认可。非常感谢! - ojblass
在我看来,这是一个不必要冗长的解决方案。顺便说一句,从技术上讲,你的解决方案并不起作用,因为1,$"_d不会做你想要的事情。我会将那行代码写成sil %d_ - ib.
@ib 为什么你说它不行?它对我有效,而且它将整个文件删除到匿名寄存器中,难道不是吗? - Mykola Golubyev
不,它不会:打开一个非空缓冲区并运行您提出的 Ex 命令 :1,$"_d - 没有任何内容将被删除。您试图像普通模式命令 d 一样使用 Ex 命令 :delete。然而,这些命令的语法和语义都是不同的!:delete 命令在命令名称之前接受一个可选范围,在命令名称之后接受一个可选的寄存器名称 (不带 "。您编写的是范围 1,$,后面跟着一个注释文本 _d(请参见 :quote)。因此,您的命令唯一的效果是将光标移动到当前缓冲区的最后一行。 - ib.

10

Vim内置了对多种脚本语言的支持 -- 以Python接口为例。

只需适当修改vim.current.buffer即可完成设置。

更具体地说:

function! Rotate()
python <<EOF
import vim, itertools
max_len = max((len(n) for n in vim.current.buffer))

vim.current.buffer[:] = [
    ''.join(n) for n in itertools.izip(
        *( n + ' ' * (max_len - len(n))
           for n in vim.current.buffer))]
EOF
endfunction

1
哇哦,我不知道 Vim 还支持除了 VimScript 之外的其他语言!还有 Perl、Tcl 和 Ruby,太棒了! - guns
1
它支持那些脚本,但不是默认开启的。您必须在构建过程中手动打开它们。 - Mykola Golubyev
没错。几乎每个发行版都有 vim-full 或 vim-enhanced 版本,我对我在这里发布的内容进行了测试,包括 Windows gvim。 - Charles Duffy
疯狂的赞扬,我将等待看是否有像四个按键一样简单的高尔夫答案获胜。我想知道我应该允许对vim构建本身做出什么样的假设才能被认为是可移植的。 - ojblass
你仍然需要在计算机上某个地方有python2X.dll才能使其工作。Python本身并没有嵌入到Vim中。 - George V. Reilly
@CharlesDuffy:为了简化你的代码,你可以使用max(map(len, vim.current.buffer))代替max((len(n) for n in vim.current.buffer)),我相信这样会更好。 - Tadeck

5
如果脚本不能满足您的需求,您可以将操作记录到寄存器中(换行符是为了可读性而添加的):
qa
1G0
xGo<Esc>p
1G0j
xGp
q

这将为您提供一个宏,您可以对上面的示例或任何具有相同长度的2行字符串运行该宏。您只需要知道字符串的长度,以便正确地迭代操作的次数。

16@a

一个相当基本的解决方案,但它可行。

1
输入域可能超过两行,并且长度相同。 - ojblass

1
以下函数执行所需的编辑操作,以“转置”当前缓冲区的内容,无论其中有多少行。
fu!T()
let[m,n,@s]=[0,line('$'),"lDG:pu\r``j@s"]
g/^/let m=max([m,col('$')])
exe'%norm!'.m."A \e".m.'|D'
sil1norm!@s
exe'%norm!'.n.'gJ'
endf

为了“高尔夫”,这是它的一行版本:

let[m,n,@s]=[0,line('$'),"lDG:pu\r``j@s"]|exe'g/^/let m=max([m,col("$")])'|exe'%norm!'.m."A \e".m.'|D'|exe'sil1norm!@s'|exe'%norm!'.n.'gJ'

0
我开发了一个 vim 插件来完成这个任务。你可以在 这里 找到它。运行 :Transpose 命令来对整个文件进行转置。

0

Charles Duffy的代码可以使用izip_longest代替izip来缩短/改进:

function! Rotate()
    :py import vim, itertools
    :py vim.current.buffer[:] = [''.join(c) for c in itertools.izip_longest(*vim.current.buffer, fillvalue=" ")]
endfunction

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接