我的最终目标是根据句子分隔符分割文本。然而,它不应该被分割成句子,而应该被分割成块。 1000 行加上当前句子的其余部分。但在下面的实验中,为了简单起见,我只使用了 2 行。
考虑 VIM 缓冲区中的以下文本:
line 1. line 1. line 1.
line 2. line 2. line 2.
line 3. line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.
执行后:
:2 | exec 'normal! )hvgg' | exec "'<,'>w /tmp/part1.txt" | exec 'normal! gvd'
结果正如预期的那样。缓冲区包含:
line 2. line 2.
line 3. line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.
但下一次运行后
:2 | exec 'normal! )hvgg' | exec "'<,'>w /tmp/part2.txt" | exec 'normal! gvd'
缓冲区包含:
line 2.
line 3. line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.
这里发生了什么?为什么它不移到第二行line 3. line 3. line 3.
?
但我希望以下文本保留在缓冲区中
line 3. line 3.
line 4. line 4. line 4.
line 5. line 5. line 5.
答案1
问题的根源在于gv
最后。这是不必要的,因为视觉选择仍然来自命令的前一部分。更糟糕的是,这并不是无害的,因为当你gv
已经处于可视模式时这样做当前和之前的视觉区域交换。意味着之前的选择被重新选择!
即使先前的选择已被删除,该选择也会应用于同一区域......或者至少是其剩余部分。在这种情况下,这将是剩余line 2.
句子中的第一个。
为了演示,请观察输入两次时会发生什么:2G)hvgggvd
。这正是命令行上发生的情况,减去磁盘写入,但使用 100% 正常模式命令。
所以无论如何我们只需要失去gv
它就可以了:
:2 | exec 'normal! )hvgg' | exec "'<,'>w! /tmp/part1.txt" | exec 'normal! d'
除此之外,该命令中还有很多可以清除的噪音。其中两个exec
s 和随附的引号不是必需的:
:2 | exec 'norm! )hvgg' | '<,'>w! /tmp/part1.txt | norm! d
更新:为了回应您的评论之一...
'<,'>
是一个逐行操作,从第一个选择线最后选择的可视区域到最后一个线同一区域的。要获得字符范围,您需要使用 back-tics,如下所示:
`<,`>
但是,这不适用于:w
...它只是逐行操作。
我想指出,这超出了您的问题范围,该问题询问为什么特定文本被删除......因此您可能想针对按字符写入文件提出一个单独的问题。
更新2:这是解决您的问题的完全不同的方法......
使用以下命令创建宏
:let @q = ")hs\<CR>\<ESC>k:let @a=@a+1\<CR>:1,.w /tmp/part\<C-R>a.txt\<CR>dgg"
- 运行此命令:
:let @a = 0
. - 将光标放在句子上,然后在正常模式下运行宏
@q
这将按照您所描述的方式进行:该句子及其上面的所有内容将被写入名为 的文件中/tmp/part1.txt
,并且保存的文本将被删除。现在移动到任何其他句子并@q
再次执行,这次会发生同样的事情,但它会写入/tmp/part2.txt
.每次运行宏时,文件名都会自动递增。您可以使用 再次重置索引:let @a = 0
。
与之前的更新一样,这并没有解决所提出的实际问题,但因为我很好,所以我提供了一些额外的帮助。(OP仍然不接受我的答案。)
答案2
我终于找到了公认的解决方案。感谢@B Layer 的合作(例如提到该w
命令仅将整行写入文件)。我已经对你的答案投了赞成票。
:let i=1
:1000 | exec 'normal! )hvggd' | exec 'if i<10 | let num="0" . i | else | let num=i | endif' | call writefile(split(@@, "\n", 1), '/tmp/test' . num . '.txt') | let i=i+1
之后,@:
如果最后一个块短于 1000 行,请按 重复该命令,您将收到错误消息。剩下的就手动保存吧。
经过 5 次处决后,我得到了预期的结果:
$ wc -l /tmp/test*.txt
1001 /tmp/test01.txt
1000 /tmp/test02.txt
1001 /tmp/test03.txt
1006 /tmp/test04.txt
1001 /tmp/test05.txt
对于那些想要使用它的人,我将简要解释关键步骤:
:1000
移至第 1000 行(块的长度应至少为 1000 行)
exec 'normal! )hvggd'
移至下一个句子的开头并向后移动一个字符,以便随后不会删除下一个句子的第一个字符。要正确处理分布在多行上的句子,whichwrap=h,l
应该进行设置,据我所知,这不是默认设置。选择从当前光标位置到开头的所有内容并将其删除。删除的内容将保存在寄存器中。
call writefile(split(@@, "\n", 1), '/tmp/test' . num . '.txt')
将寄存器内容(块)写入文件
我的假设是不会超过 99 个块。如果需要更多,请根据需要调整与padding相关的部分。