Can I transpose a file in Vim?

2019-03-12 22:42发布

问题:

I know I can use AWK but I am on a Windows box. I am making a function for others that may not have AWK. I also know I can write a C program but I would love not have to create maintain and compile something for a little Vim utility I am making.

The original file might be

THE DAY WAS LONG 
THE WAY WAS FAST

and it would become

TT
HH
EE

DW
AA
YY

WW
AA
SS

LF
OA
NS
GT

UPDATE: Golf rules apply to selecting correct answer.

UPDATE: Python fans should check out Mr. Duffy's answer below.

回答1:

Here is a command in Vim language. So you don't have to compile Vim with +python support.

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()

Put this in newly created .vim file inside vim/plugin dir or put this to your [._]vimrc.
Execute :TransposeBuffer to transpose current buffer



回答2:

Vim support for a number of scripting languages built in -- see the Python interface as an example.

Just modify vim.current.buffer appropriately and you're set.

To be a little more specific:

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


回答3:

If scripts don't do it for you, you could record the actions to a register (the carriage returns are added for readability):

qa
1G0
xGo<Esc>p
1G0j
xGp
q

This will give you a macro that you could run against the example above, or any 2-line strings of the same length. You only need to know the length of the string so you can iterate the operation the correct number of time

16@a

A fairly basic solution, but it works.



回答4:

The following function performs required editing operations to "transpose" the contents of the current buffer.

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

Below is its one-line version,

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'


回答5:

Charles Duffy's code could be shortened/improved using izip_longest instead of izip:

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


回答6:

I've developed a vim plugin to do it. You can find it here. Run :Transpose to transpose the whole file.