我试图交错三组文本行。例如,从
开始a
a
a
b
b
b
c
c
c
要
a
b
c
a
b
c
a
b
c
有没有一种有效的方法呢?
答案 0 :(得分:6)
在我~/.vim
文件深处的某个地方,我有一个:Interleave
命令(后面附有)。没有任何参数:Interleave
将只是正常交错。有两个参数,它将如何指定将多少组合在一起。例如:Interleave 2 1
将从顶部开始排2行,然后从底部开始与1行交错。
现在解决你的问题
:1,/c/-1Interleave
:Interleave 2 1
1,/c/-1
范围从匹配字母c
的第一行上方的第一行和第一行开始。:1,/c/-1Interleave
基本上交错了a
和b
的:Interleave 2 1
这次是范围是整个文件。:Interleave 2 1
将a
和b
的混合组与c
组进行交错。混合比为2比1。 :Interleave
代码如下。
command! -bar -nargs=* -range=% Interleave :<line1>,<line2>call Interleave(<f-args>)
fun! Interleave(...) range
if a:0 == 0
let x = 1
let y = 1
elseif a:0 == 1
let x = a:1
let y = a:1
elseif a:0 == 2
let x = a:1
let y = a:2
elseif a:0 > 2
echohl WarningMsg
echo "Argument Error: can have at most 2 arguments"
echohl None
return
endif
let i = a:firstline + x - 1
let total = a:lastline - a:firstline + 1
let j = total / (x + y) * x + a:firstline
while j < a:lastline
let range = y > 1 ? j . ',' . (j+y) : j
silent exe range . 'move ' . i
let i += y + x
let j += y
endwhile
endfun
答案 1 :(得分:1)
我今晚独立地遇到了这个问题。我的并不像一些答案那么优雅,但我认为它更容易理解。它做了很多假设,所以这有点像黑客:
@
。C)假设您可以轻松识别 最大行长度,然后将所有行填充为该长度(例如 也许用%!进入awk等等,使用printf)
%s/$/@
%s/@/\r
%s/^ *//g
%s/ *$//g
答案 2 :(得分:1)
如果您有xclip
,则可以剪切线条并使用paste
进行交错:
"+d
将其剪切到剪贴板!paste -d '\n' /dev/stdin <(xclip -o -selection clipboard)
答案 3 :(得分:0)
这是一个“oneliner”(差不多),但你必须为每个唯一的行减1重做它,在你的例子中2次。也许没有用,但我认为这是一个很好的练习,可以更多地了解VIM中的模式。它处理所有类型的行,只要整行是唯一的(例如mno
和mnp
是两个唯一的行)。
首先确保这一点(并且不将/
映射到任何内容或该行中的任何其他内容):
:set nowrapscan
然后映射,例如这些(应该是递归的,不 nnoremap
):
<C-R>
和<CR>
应输入字面
模式中的\v
表示“非常神奇”,@!
负向前瞻。 \2
使用第二个括号中的内容。
:nmap ,. "xy$/\v^<C-R>x$<CR>:/\v^(<C-R>x)@!(.*)$\n(\2)$/m-<CR>j,.
:nmap ,, gg,.
然后执行,,
次数,在您的示例中执行2次。一个适用于所有b
,一个适用于所有c
。
编辑:对映射的解释。我将在问题中使用该示例,就好像它已经使用此映射运行了一次 一次运行后:
1. a
2. b
3. a
4. b
5. a
6. b
7. c
8. c
9. c
然后光标位于最后a
(第5行),当键入,,
时,它首先返回到第一行,然后运行,.
的映射,并且该映射这样做:
"xy$ # yanks current line (line 1) to reg. "x" ("a") "
/\v^<C-R>x$<CR> # finds next line matching reg. "x" ("a" at line 3)
:/\v^(<C-R>x)@!(.*)$\n(\2)$/m-<CR>
# finds next line that have a copy under it ("c" in line 7) and moves that line
# to current line (to line 3, if no "-" #after "m" it's pasted after current line)
# Parts in the pattern:
- ^(<C-R>x)@!(.*)$ # matches next line that don't start with what's in reg. "x"
- \n(\2)$ # ...and followed by newline and same line again ("c\nc")
- m-<CR> # inserts found line at current line (line 3)
j # down one line (to line 4, where second "a" now is)
,. # does all again (recursive), this time finding "c" in line 8
...
,. # gives error since there are no more repeated lines,
# and the "looping" breaks.
答案 4 :(得分:0)
将以下内容作为 interleave.awk
放在您的路径中,使其可执行。
#!/usr/bin/awk -f
BEGIN { C = 2; if (ARGC > 1) C = ARGV[1]; ARGV[1]="" }
{ g = (NR - 1) % C; if (!g) print $0; else O[g] = O[g] $0 "\n" }
END { for (i = 1; i < C; i++) printf O[i] }
然后从 vim
突出显示视觉模式中的行,然后调用 :'<,'>!interleave.awk 3
,或将 3 替换为要交错的多个组(或留空 2)。
您要求一种有效的方法。撇开解释语言不谈,这可能是交错任意行的最有效算法 - 第一组立即打印,节省一些 RAM。如果 RAM 非常宝贵(例如,大量行或太多行),您可以将偏移量存储到每行的开头,并且如果行具有一致的明确定义的长度(至少在组内),您就不会甚至需要存储偏移量。然而,这样文件只被扫描一次(允许使用标准输入),并且 CPU 复制数据块的速度很快,而文件指针操作可能每个都需要上下文切换,因为它们通常必须触发系统调用。>
也许最重要的是,代码简单而简短 - 阅读和实现的效率通常是最重要的。
编辑:看起来其他人已经找到了相同的解决方案 - 刚刚在搜索引擎中重新构建问题以查看我是否遗漏了一些明显的问题时发现了 https://stackoverflow.com/a/16088069/118153。