当我搜索某些内容并捕获行号时如何检查行是否匹配?

How to check if line has match when I search something and catch the linenumber?

我想用 1 个或多个匹配项捕获缓冲区中的所有行号。

直到现在我都使用这个代码:

let lines = []

for line in range(startline, endline)
  let @e = ''
  :redir @e
  :silent exe line.'s/'.mysearch.'/&/gne'
  :redir END
  if matchstr(@e, 'match') != ''
    let nummer = matchstr(@e, '\d\+\ze')
    if nummer > 0
      call add(lines, ''.line.'')
    endif
  endif
endfor

但这速度太慢了。
有没有办法更快地做到这一点?
(p.e。静默执行搜索:1 表示匹配并捕获行号,0 表示不匹配)

这是一个可以做到这一点的函数:

function! GetLines(mysearch) range
  let lines = []
  let l:i=a:firstline
  while l:i<=a:lastline
    if matchstr(getline(l:i),a:mysearch) != ''
      call add(lines, l:i)
    endif
    let l:i+=1
  endwhile
  let lines
endfunction

您可以通过以下方式调用它:

:N°1,N°2 call GetLines("pattern")

我会在 getline(1,'$') 上使用 filter()。要获取行号,我们可以使用 map() 和新的 (Vim 8) lambda。或者可能只是 filter (range (1, line ('$')), 'getline (v:val) =~ regex')

您几乎找不到 vim 更快的方法,尤其是手动循环。

编辑:

例如,在一个 7850 行长的 vim 日志文件中搜索 "home",它出现了 6438 次:

  • echo lh#time#bench('filter', range (1, line ('$')), 'getline (v:val) =~ "home"') 需要 0.033297s
  • :echo lh#time#bench('filter', range (1, line ('$')),{ idx, val -> getline (val) =~ 'home'})[1] 需要 0.078506 秒——有点慢,但正则表达式更简单
  • let r=[] + echo = lh#time#bench('map', range(1, line('$')), 'substitute(getline(v:val), "home", "\=add(g:r, v:val)", "")')[1] 需要 8.841528 秒——我想这就是您使用手动循环得到的结果
  • :let start=reltime()|:let lines=filter(map(getline(1, '$'), {idx, val -> val =~ "home"?idx:0}), "v:val>0")|:echo reltimefloat(reltime(start, reltime())) 需要:0.075132s

顺便说一句,我还在我的 vim 8.0-134 上测量了之前的片段,match() 比普通的 [=21] 慢一点(不是很明显) =].不要问我为什么。

IOW,神秘的正则表达式似乎是最快的解决方案,当性能很重要时,应不惜一切代价避免手动循环。