在vim中使用正则表达式匹配带括号的块

zdwk9cvp  于 2023-08-05  发布在  其他
关注(0)|答案(5)|浏览(124)

我尝试匹配某个(和vim在使用运动%时找到的匹配)之间的内容。
更具体地说,我正在寻找一个看起来像这个假设的/someKeyword (\{pair}\(.*\))\{pair}/的正则表达式,如果有像\{pair}这样的修饰符,当应用于正则表达式中的两个正好两个字符时,使第二个字符只匹配第一个字符的括号(%)。
我正在寻找的模式应该与someKeyword后面的第一个括号的内部内容相匹配(注:它应该处理的代码总是正确地放在括号中),如示例所示:
对于someKeyword ("aaa"),子匹配将匹配"aaa"。同样,someKeyword ("aaa)")将匹配"aaa)"someKeyword(("double-nested stuff"))将匹配("double-nested stuff")
但也包括以下情况:

(
  someKeyword("xyz"))

字符串
其中它应该匹配"xyz"
有没有办法在正则表达式中使用vim的匹配括号功能?如果没有,还有什么其他解决方案可以实现这一目标?
编辑1:匹配的内容可以跨几行。

brc7rcf0

brc7rcf01#

你可以用宏很容易地做到这一点。你可以用regexp搜索关键字,然后搜索文本的开头,然后是结尾,然后你可以采取任何你想要的行动。
例如,要猛拉所需的文本:

qa/somekeyword\s*
/(
lvh%hyq

字符串
现在,无论何时使用@a宏,您都可以提取下一个感兴趣的文本。

wtlkbnrh

wtlkbnrh2#

突出显示代码块的正则表达式方法如下所示,用于{}嵌套大括号:
/{[A-Za-z \n]\+\(\(\({[A-Za-z \n]\+\)\{3}\([A-Za-z \n]\+}\)\{3}\)\+[A-Za-z \n]\+\)\+}
对于()简单嵌套括号:
/([A-Za-z \n]\+\(\(\(([A-Za-z \n]\+\)\{3}\([A-Za-z \n]\+)\)\{3}\)\+[A-Za-z \n]\+\)\+)
其中\{3}...\{3}必须替换为所需的嵌套级别。
您还可以修改[A-Za-z \n]\+组以增加字符匹配。
如此这般。
最好的问候!

rm5edbpk

rm5edbpk3#

这在vim正则表达式中是不可能的(因为允许这种嵌套结构的语言不是正则的),但是在perl提供的“正则”表达式中是可能的(以及其他我不太了解的语言),并且perl可以从vim内部使用。我不喜欢vim-perl绑定(因为它非常有限),但如果你知道应该工作的所有情况,那么你可以使用perl正则表达式的递归功能(需要更新的perl,我有5.12*):

perl VIM::Msg($+{"outer"}) if $curbuf->Get(3) =~ /someKeyword\((?'outer'(?'inner'"(?:\\.|[^"])*"|'(?:[^']|'')*'|[^()]*|\((?P>inner)*\))*)\)/

字符串
注意,如果可以避免这样的正则表达式,你应该这样做(因为你太依赖re编译器了),所以我建议直接使用vim motions:

let s:reply=""
function! SetReplyToKeywordArgs(...)
    let [sline, scol]=getpos("'[")[1:2]
    let [eline, ecol]=getpos("']")[1:2]
    let lchar=len(matchstr(getline(eline), '\%'.ecol.'c.'))
    if lchar>1
        let ecol+=lchar-1
    endif
    let text=[]
    let ellcol=col([eline, '$'])
    let slinestr=getline(sline)
    if sline==eline
        if ecol>=ellcol
            call extend(text, [slinestr[(scol-1):], ""])
        else
            call add(text, slinestr[(scol-1):(ecol-1)])
        endif
    else
        call add(text, slinestr[(scol-1):])
        let elinestr=getline(eline)
        if (eline-sline)>1
            call extend(text, getline(sline+1, eline-1))
        endif
        if ecol<ellcol
            call add(text, elinestr[:(ecol-1)])
        else
            call extend(text, [elinestr, ""])
        endif
    endif
    let s:reply=join(text, "\n")
endfunction
function! GetKeywordArgs()
    let winview=winsaveview()
    keepjumps call search('someKeyword', 'e')
    setlocal operatorfunc=SetReplyToKeywordArgs
    keepjumps normal! f(g@i(
    call winrestview(winview)
    return s:reply
endfunction


您可以使用类似

let savedureg=@"
let saved0reg=@0
keepjumps normal! f(yi(
let s:reply=@"
let @"=savedureg
let @0=saved0reg


而不是operatorfunc来保存和恢复寄存器,但是上面的代码保留了所有的寄存器和标记不变,我不能用saved* 的东西来保证。它还保证,如果您删除text周围的join(),您将保存有关NULL位置的信息(当然,如果您关心它们)。这是不可能的寄存器变量。

wswtfjt7

wswtfjt74#

大多数regex方言都不能做到这一点。在.NET实现中是可能的(参见here),但是非常非常丑陋,在我看来不应该使用。

cygmwpex

cygmwpex5#

正如Jens所说,这不能用Vim正则表达式来完成。
但是,如果你想做一些特殊的处理,你可以做以下事情:

  • 找到您感兴趣的左括号,然后运行一个宏,使用%绑定在左括号之后和右括号之前插入一些字符(比如^@)。
  • 然后相应地修改正则表达式。

相关问题