在 vim 中使用 perl 风格的正则表达式

By | 2015-11-15

前言

vim 的搜索和替换功能强大突破天际, 但是正则语法实在怪异, 既不像平常熟知的 perl 风格, 也不像 grep 风格, 每次都记不住语法

先来体验一下两者的巨大差别:

perl 风格:

(ab+?c)(?!ing)

vim 风格:

\(ab\{-1,}c\)\%(ing\)\@!/

于是某Z 坐不住了打算定制一下

buffer 内搜索和替换

这个有现成的好用的东西了, 使用 othree/eregex.vim 即可

顺便映射几个按键比较符合个人习惯:

nnoremap / /\v
nnoremap ? :M/
nnoremap <leader>vf :M/<c-r><c-w>
nnoremap <leader>vr :.,$S///gec<left><left><left><left><left>

关于 \v 可以 :h magic

没有直接映射到 / 上, 是因为这货不支持 incsearch

另外, 这货也直接支持 :G:V 命令

sort

vim 自带的 :sort 功能非常强大, 可以用正则来指定排序规则

所以, 我们也可以用 eregex 来包装一下

function! ZF_Plugin_eregex_sort(bang, line1, line2, args)
    let cmd = a:line1 . ',' . a:line2 . 'sort' . a:bang . ' '
    if len(a:args) <= 0 || a:args[0] != '/'
        let cmd .= a:args
    else
        let match = matchstrpos(a:args, '\%(/\)\@<=.*\%(/\)\@=')
        if match[1] >= 0
            let cmd .= '/' . E2v(match[0]) . '/ ' . strpart(a:args, match[2] + 1)
        else
            let cmd .= '/' . E2v(strpart(a:args, 1)) . '/'
        endif
    endif

    execute cmd
endfunction
command! -nargs=* -range=% -bang Sort :call ZF_Plugin_eregex_sort('<bang>', <line1>, <line2>, <q-args>)

使用方法和默认的 :sort 完全一样

全工程搜索和替换

试用过很多 vim 全工程搜索替换的插件, 最终还是 dkprice/vim-easygrep 最适合我

grep 本身也支持添加 -P 参数来使用 perl 风格正则表达式, 可惜 easygrep 没有支持, 于是本人又坐不住稍微修改了下
(原 repo 已合并该功能, 参见 这里)

使用方法很简单:

  1. 需要安装 othree/eregex.vim 插件

  2. 添加以下设置:

    set grepprg=grep\ -n\ $*\ /dev/null
    let g:EasyGrepCommand=1
    let g:EasyGrepPerlStyle=1
  3. 然后正常的使用 :Grep:Replace 即可

注意事项:

  • Windows 下需要安装 cygwin 并且添加 cygwin/bin 的路径到系统 PATH 里面 (MinGW 没试过, 应该也行)
  • grep 必须是 2.5.3 以上的 GNU 版本

    mac 下默认是 BSD 版本, 不支持 perl 风格正则, 所以必须手动安装 GNU 版本

    可以使用 brew 进行安装, 也可以自行下载 grep 源码编译, 具体请自行 Google 吧

多行正则搜索

有时候还是需要跨行进行正则搜索 (例如 aaa.*\n.*bbb), 然而 grep 并不支持这个, 所幸我们还有 pcregrep,
这边顺便提供一个个人的配置, 保持和 easygrep 的使用习惯一致, 不过只支持查找不支持替换

function! ZF_Plugin_easygrep_pcregrep(expr)
    let expr = a:expr
    let expr = substitute(expr, '"', '\\"', 'g')

    let cmd = 'pcregrep --buffer-size=16M -M -r -s -n'
    if match(expr, '\C[A-Z]') < 0
        let cmd .= ' -i'
    endif
    let ignoreList = split(g:EasyGrepFilesToExclude, ',')
    for ignore in ignoreList
        let ignore = substitute(ignore, '\.', '\\.', 'g')
        let ignore = substitute(ignore, '\*', '.*', 'g')

        let cmd .= ' --exclude-dir="' . ignore . '"'
        let cmd .= ' --exclude="' . ignore . '"'
    endfor
    let cmd .= ' "' . expr . '" *'

    let result = system(cmd)
    let qflist = []
    let vim_pattern = E2v(a:expr)
    for line in split(result, '\n')
        let file = substitute(matchstr(line, '^[^:]\+:'), ':', '', 'g')
        let file_line = substitute(matchstr(line, ':[0-9]\+:'), ':', '', 'g')
        if strlen(file) <= 0 || strlen(file_line) <= 0
            continue
        endif
        let text = substitute(line, '^\[^:\]*:\[^:\]*:\(.*\)$', '\1', 'g')
        let qflist += [{
                    \ 'filename' : file,
                    \ 'lnum' : file_line,
                    \ 'text' : text,
                    \ 'pattern' : vim_pattern,
                    \ }]
    endfor
    call setqflist(qflist)
    if len(qflist) > 0
        execute ':silent! M/' . a:expr
        copen
    else
        echo 'no matches for: ' . a:expr
    endif
endfunction
command! -nargs=+ ZFGrepExt :call ZF_Plugin_easygrep_pcregrep(<q-args>)
try
    if match(system('pcregrep --version'), '[0-9]\+\.[0-9]\+') < 0
        nnoremap <leader>vge :echo 'pcregrep not installed'<cr>
    else
        nnoremap <leader>vge :ZFGrepExt<space>
    endif
endtry

转载请注明来自: http://zsaber.com/blog/p/73

既然都来了, 有啥想法顺便留个言呗? (无奈小广告太多, 需审核, 见谅)

Category: vim

One thought on “在 vim 中使用 perl 风格的正则表达式

  1. asins

    谢谢,以前在一文章中看到搜索时使用perl风格正则,忘了后就一直没搜索到使用方式,今天终于在你这找到了,实在是无法适应VIM自身的正则风格

    Reply

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注