如何将列数据分成多列

时间:2015-02-01 05:40:01

标签: vim multiple-columns vi

我有以下数据块作为示例:

0.1 A
0.2 B
0.3 C

0.1 E
0.2 F
0.3 G
...

我希望在出现新行时将它们分成多行

0.1 A E H ...

0.2 B F I ...

0.3 C G J ...

我可以使用vim中的列编辑模式( Ctrl + V )手动将数据粘贴到新列中以用于简单的情况,但是当新行时/r段变为数百个,这是不可能的。 有没有办法让vim每次看到空行时都将以下数据移动到新列?

非常感谢。

3 个答案:

答案 0 :(得分:2)

你可以通过很多方式完成所需的工作,但这是我提出的方法。

for i in range(1, 3)
    exe '4,$g/\v0\.' . i . '/exe "normal! ^dWd\<Space>4G?0.' . i . '\<CR>A\<Space>\<Esc>p"'
endfor

这仅适用于您给我的具体案例,但我会告诉您如何根据您的需要进行调整。以下是for循环中间命令的细分:

exe '4,$g/\v0\.' . i .
 ^      ^          ^
 |      |          |
 |      |          |
 |      |  Part of the for loop, in this case it searches for 0.x
 |      |
 |  Takes in a command line range,
 |  (line 4 through end of file)
 |  searches for pattern in between
 |  two slashes, (like :s command)
 |  And executes the following command
 |  on the searched lines.
 |
Runs a vim command from string

现在是g命令后面的命令:

'/exe "normal! ^dWd\<Space>4G?0.' . i . '\<CR>A\<Space>\<Esc>p"'

首先,它使用exe命令执行普通命令。为什么这很有用是因为<Esc>之类的特殊键未在正常命令中翻译。但是,在exe命令中使用时,它们会被翻译。这组击键在第4行和匹配0.x的文件末尾之间的每一行上执行。按顺序,以下是normal命令中每次击键的内容:

  • ^ - 转到行尾
  • dW - 删除WORD (在这种情况下,删除0.x)
  • d\<Space> - 删除单个字符,在本例中是0.x之后的字母。 (如果您想复制的不仅仅是一个字符,请更改此选项)
  • 4G - 转到文件的第4行。 (在前三行数据之后,如果顶部有三行以上,请更改此内容)
  • ?0.' . i . '\<CR> - 向后搜索相应的0.x
  • A\<Space>\<Esc>p - 在行尾添加空格,退出插入,并在行尾添加已删除的字符

如果您需要更多信息,请参阅以下相关的vim帮助主题:

:help :g
:help cmdline-ranges
:help :normal
:help :exe

答案 1 :(得分:1)

在vim guru找到像this之类的单行解决方案之前,您可能希望使用以下自定义函数来执行每个索引整数的连接:

"range function to join lines ordered by a "X" pattern where X is an integer 
"This function is an attempt to answer SO question #28259859 by F14r3
function! F14r3_28259859() range
  let pmatch = ""
  let acc = ""
  let oline = a:lastline + 1
  for linenum in range(a:firstline, a:lastline)
    let curr_line   = getline(linenum)
    let cmatch = matchlist(curr_line,'\(\d\+\) \(.\+\)')
    if (acc=="")
      let acc = curr_line
      let pmatch = cmatch[1]
    else
      if (pmatch==cmatch[1])
        let acc = acc . " " . cmatch[2]
        if (linenum==a:lastline)
          call setline(oline,acc)
        endif
      else
        call setline(oline,acc)
        let pmatch = cmatch[1]
        let acc=curr_line
        let oline = oline + 1
      endif
    endif
  endfor
  return 0
endfunction

以下是使用它的步骤。

0-将函数存储在缓冲区中并获取它(:so%

1-从这样的输入文件开始:

1 Aaaa
2 Bbbbbbbbbbbbbbbbb
3 Cc
1 Ee  ee
2 F
3 G

2-运行此Ex-cmd - &gt; :排序,您得到:

1 Aaaa
1 Ee  ee
2 Bbbbbbbbbbbbbbbbb
2 F
3 Cc
3 G

3-视觉选择按行( V )排序的行并调用(输入)该功能(输入F后,请通过标签请求完成)像这样:

'<,'>call F14r3_28259859()

4-检查结果,它应该是这样的(连接的行位于缓冲区的末尾):

1 Aaaa
1 Ee  ee
2 Bbbbbbbbbbbbbbbbb
2 F
3 Cc
3 G
1 Aaaa Ee  ee
2 Bbbbbbbbbbbbbbbbb F
3 Cc G

5-如果您对结果感到满意,请按 gv (重新选择)和 d

删除已排序的行
1 Aaaa Ee  ee
2 Bbbbbbbbbbbbbbbbb F
3 Cc G

6-请注意,此函数仅包含简单整数的正则表达式;你应该修改它以接受你的格式&#34; 0.1&#34;带点。

希望它有所帮助。

答案 2 :(得分:0)

这是一个不需要大量脚本并且只使用简单命令和基于模式的范围的解决方案。以下内容将对整个文件进行操作,但它可以用于一系列行。

首先,因为看起来每一行在行的开头都有一个很好的索引,所以首先要对文件进行排序:

:sort

接下来,遍历每个索引行,并将其连接到下一个索引的所有行(因为文件现在已经排序,所有这些行将在一起,并且在下一个索引之前)。为了方便起见,只需附加&#34; next&#34;文件末尾的一行上未使用的索引。在这种情况下,0.4。然后运行以下命令:

:for i in range(1,3) | exec 'g#0\.'.i.'#.,/0\.'.(i+1).'/-1j' | endfor

这将在匹配&#34; 0.1&#34;,&#34; 0.2&#34;,&#34; 0.3&#34;的行上运行:g命令反过来。运行的命令是(例如):.,/0.2/-1j,表示&#34;加入此行的所有行,直到下一次匹配前的行为&#39; 0.2&#39;&#34;。因此,您的文件现在将如下所示:

0.1 A 0.1 E 0.1 H
0.2 B 0.2 F 0.2 I
0.3 C 0.3 G 0.3 J
0.4

请注意,我在排序后添加了0.4,以便在处理最后一组实际数据行时允许(i + 1)范围匹配。

现在很容易删除额外的索引列:

:%s# 0\.\d\+##g

最后,只需删除我们在排序后添加的额外索引。