这是我的字符串:"this is my sentence"
我想要这个输出:"sentence my is this"
我想在一行中(在缓冲区中)选择几个单词,然后逐个单词反转它。
谁能帮我?
最佳答案
目前尚不清楚上下文是什么:您可能正在谈论缓冲区中一行中的文本,或者是存储在VimScript变量中的字符串。
注意:对该问题的不同解释导致了各种方法和解决方案。
有一些“旧的更新”大约在中途才开始,而该部分前面提到的插件已将这些更新或多或少地作废了。我将它们留在里面是因为它们可能为某些人提供有用的信息。
全线更换
因此,要将当前行中的文本存储在vimscript变量中的当前缓冲区中,您可以
let words = getline('.')
然后颠倒他们的顺序,您只需
let words = join(reverse(split(words)))
如果您想用反白的字词替换当前行,则可以
call setline('.', words)
您可以通过以下方式进行一些难以理解的操作:
call setline('.', join(reverse(split(getline('.')))))
甚至定义一个执行该操作的命令
command! ReverseLine call setline('.', join(reverse(split(getline('.')))))
部分行(按字符)选择
如“旧的更新”部分中所述,在字符或逐块视觉选择上运行常规命令可能非常复杂,前者是OP希望在此处执行的操作。像
:substitute
这样的Ex命令将在整行上运行,即使使用按字符显示的视觉选择(以未移动的v开头)仅选择了该行的一部分也是如此。在下面的OP评论后,我意识到,使用部分行字符明智的选择来反转单词可以很容易地完成
:s/\%V.*\%V./\=join(reverse(split(submatch(0))))/
其中
RE中的
\%V
与视觉选择的某些部分匹配。显然,这不会扩展到选择中的最后一个字符之后:省略最后一个.
将排除最后一个选择的字符。替换开始处的
\=
表示应将其作为vimscript表达式进行评估,并带有一些differences。submatch(0)
返回整个匹配项。这有点像perl的$&
,$1
等,但是它仅在评估替换时可用。我认为这意味着它只能在:substitute
命令或对substitute()
的调用中使用因此,如果您想对单行选择进行替换,则效果会很好。您甚至可以使用
...\=system(submatch(0))
通过系统命令来传递选择内容。多行按字符选择
这似乎也适用于多行字符明智的选择,但是您必须小心删除范围(从可视模式进入时,vim在命令开头放置的
'<,'>
)。您只想在视觉选择开始的那一行上运行命令。您还必须使用\_.*
而不是.*
以便在换行符之间进行匹配。逐块选择
对于逐块选择,我认为没有合理便捷的方式来进行选择。我编写了一个插件,该插件可以提供一种在任何可视选择上运行任意Ex命令的方式,使它看起来像是整个缓冲区的内容,从而使这些编辑工作更轻松。
它在https://github.com/intuited/visdo上可用。当前没有包装,并且在vim.org上尚未提供,但是您可以
git clone
将其包装并将其内容(减去README文件)复制到您的vimdir中。如果使用vim-addon-manager,只需在vim-addons目录中克隆visdo,随后便可以使用
ActivateAddons visdo
。我提出了将其添加到VAM插件存储库的请求,因此在某些时候您将可以省去克隆,而只需执行ActivateAddons visdo
即可。该插件添加了一个
:VisDo
命令,该命令应以另一个命令为前缀(类似于:tab
或:silent
的工作方式)。以VisDo
开头运行命令将使该命令在仅包含视觉选择的当前内容的缓冲区上运行。命令完成后,将缓冲区的内容粘贴到原始缓冲区的可视选择中,并删除临时缓冲区。因此,要使用VisDo完成OP的目标,您可以这样做(选择要反转的单词,并使用上面定义的ReverseLine命令):
:'<,'>VisDo ReverseLine
旧更新
...之前的更新如下...警告:冗长,有些过时,而且基本上是不必要的...
OP的编辑更清楚地表明,此处的目标是能够反转视觉选择,尤其是按字符选择的视觉选择中包含的单词。
这绝对不是一个简单的任务。当我第一次开始使用vim时,vim并不容易使我感到困惑。我猜这是因为它的根源仍然非常多地存在于
ed
及其前代和后代的面向行的编辑功能中。例如,即使您处于按字符方式或按块方式(ctrlv)的视觉选择模式下,替代命令:'<,'>s/.../.../
也将始终在整行上工作。Vimscript确实可以找到任何“标记”的列号,包括视觉选择的开始(
'<
)和视觉选择的结束('>
)。据我所知,这就是其支持的极限。没有直接的方法来获取视觉选择的内容,也没有办法用其他东西代替视觉选择。当然,您可以使用普通模式命令(y
和p
)来完成这两项操作,但是这种掩体很容易注册并且有点混乱。但是然后您可以保存初始寄存器,然后在粘贴后还原它们...因此,基本上,您必须走极端的长度才能处理部分线,而整条线则很容易做到。我怀疑最好的方法是编写一条命令,将视觉选择复制到新缓冲区中,在其上运行其他命令,然后用结果替换原始缓冲区的视觉选择,删除临时缓冲区。从理论上讲,该方法应适用于按字符选择和按块选择以及已受支持的按行选择。但是,我还没有这样做。
这个40行代码块声明一个命令
ReverseCharwiseVisualWords
,可以从可视模式调用该命令。仅在按字符显示的视觉选择完全在一行上时才起作用。它的工作原理是获取包含视觉选择的整个行(使用getline()
),在其选定部分上运行参数化转换函数(ReverseWords
),然后将整个部分转换的行粘贴回去。回顾一下,我认为可能值得采用y
/ p
路线以获取更多功能。" Return 1-based column numbers for the start and end of the visual selection.
function! GetVisualCols()
return [getpos("'<")[2], getpos("'>")[2]]
endfunction
" Convert a 0-based string index to an RE atom using 1-based column index
" :help /\%c
function! ColAtom(index)
return '\%' . string(a:index + 1) . 'c'
endfunction
" Replace the substring of a:str from a:start to a:end (inclusive)
" with a:repl
function! StrReplace(str, start, end, repl)
let regexp = ColAtom(a:start) . '.*' . ColAtom(a:end + 1)
return substitute(a:str, regexp, a:repl, '')
endfunction
" Replace the character-wise visual selection
" with the result of running a:Transform on it.
" Only works if the visual selection is on a single line.
function! TransformCharwiseVisual(Transform)
let [startCol, endCol] = GetVisualCols()
" Column numbers are 1-based; string indexes are 0-based
let [startIndex, endIndex] = [startCol - 1, endCol - 1]
let line = getline("'<")
let visualSelection = line[startIndex : endIndex]
let transformed = a:Transform(visualSelection)
let transformed_line = StrReplace(line, startIndex, endIndex, transformed)
call setline("'<", transformed_line)
endfunction
function! ReverseWords(words)
return join(reverse(split(a:words)))
endfunction
" Use -range to allow range to be passed
" as by default for commands initiated from visual mode,
" then ignore it.
command! -range ReverseCharwiseVisualWords
\ call TransformCharwiseVisual(function('ReverseWords'))
更新2
事实证明,使用
y
和p
进行操作要简单得多,所以我认为我也会发布它。警告:我没有对这一切进行过彻底的测试,因此可能存在一些极端情况。此函数替换上一个代码块中的
TransformCharwiseVisual
(及其某些依赖项)。从理论上讲,它也应该适用于逐块选择-由传递的Transform
函数使用行定界符来做适当的事情。function! TransformYankPasteVisual(Transform)
let original_unnamed = [getreg('"'), getregtype('"')]
try
" Reactivate and yank the current visual selection.
normal gvy
let @" = a:Transform(@")
normal gvp
finally
call call(function('setreg'), ['"'] + original_unnamed)
endtry
endfunction
因此,您只需添加第二个命令声明
command! -range ReverseVisualWords
\ call TransformYankPasteVisual(function('ReverseWords'))
切线相关的血腥细节
请注意,像这里使用的那样,高级函数的实用性在一定程度上受到以下事实的限制:没有方法(简单或已建立)在vimscript中声明内联函数或代码块。如果该语言不打算以交互方式使用,那么这并不是一个限制。您可以编写一个将其字符串参数替换为字典函数声明并返回该函数的函数。但是,不能使用常规的调用语法来调用字典函数,而必须将其传递给
call call(dictfunct, args, {})
。注意:上面给出的最新更新使上述代码过时。有关更干净的方法,请参阅旧更新之前的各个部分。
关于regex - Vim:反转字符串(按字),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5532431/