Кажется, что нет способа достичь sh такого прямого результата, используя один split()
. Фактически, документы для split()
упоминают эту конкретную ситуацию сохранения разделителя с:
Если вы хотите сохранить разделитель, вы также можете использовать \zs
в конце шаблона:
:echo split('abc:def:ghi', ':\zs')
['abc:', 'def:', 'ghi']
Сказав это, использование как взглядов вперед, так и взглядов назад действительно действительно работает. В вашем примере у вас есть синтаксическая ошибка. Так как вы используете режим verymagi c, вам не следует экранировать @
, поскольку он уже особенный. (Спасибо @ user938271 за указание на это!)
Это работает:
:echo split('barFOObar', '\v((FOO)@<=|(FOO)@=)')
" --> ['bar', 'FOO', 'bar']
Относительно использования маркеров для \zs
и \ze
:
:echo split('barFOObar', '\v(FOO\zs|\zeFOO)')
" --> ['barFOO', 'bar']
Итак, первая проблема, с которой вы столкнулись, состоит в том, что оба выражения на каждой стороне |
соответствуют одному и тому же тексту "FOO", поэтому, поскольку они идентичны, первые выигрыши и вы получите это на левой стороне.
Измените порядок, и вы получите его на правой стороне:
:echo split('barFOObar', '\v(\zeFOO|FOO\zs)')
" --> ['bar', 'FOObar']
Теперь возникает вопрос, почему второй токен "FOObar" не разделяется, так как он повторное сопоставление (случай подсвечивания разделяет этот, верно?)
Ну, ответ в том, что он на самом деле снова разделяется, но он совпадает в первом случае \zeFOO
еще раз и производит разделение с пустая строка. Вы можете увидеть это, передав ключевой аргумент:
:echo split('barFOObar', '\v(\zeFOO|FOO\zs)', 1)
" --> ['bar', '', 'FOObar']
Один вопрос, который до сих пор остается без ответа, заключается в том, почему lookahead / lookbehind работает , а \zs
и \ze
нет. Я думаю, что я как-то учел это в этом ответе , чтобы использовать регулярное выражение в группах синтаксиса.
Это не будет работать, потому что Vim не будет сканировать один и тот же текст дважды, пытаясь сопоставить другое регулярное выражение.
Несмотря на то, что в \zs
полученное совпадение включает в себя только bar
, Vim необходимо использовать FOO
, чтобы иметь возможность сопоставить это регулярное выражение, и не будет, если оно уже сопоставило его с другая половина шаблона.
Вид сзади с \@<=
отличается. Причина, по которой он работает, заключается в том, что Vim сначала ищет bar
(или любой другой текст, который он рассматривает), а затем оглядывается назад, чтобы увидеть, совпадает ли FOO
. Таким образом, шаблон привязывается к bar
, а не FOO
и не страдает от проблемы с попыткой начать сопоставление в области, которая уже соответствует другому выражению.
Вы можете легко визуализировать эту разницу, выполняя поиск с Vim. Попробуйте это:
/\v(\zeFOO|FOO\zs)
и сравните его с этим:
/\v((FOO)@<=|(FOO)@=)
Вы заметите, что последний будет соответствовать как до , так и после FOO, в то время как первый не будет.
Сравните это, например, с Python [re.split
] ... в Python, '(FOO)'
в скобках также будет работать для this.
Двигатели регулярных выражений Vim и Python - разные звери ...
Многие ограничения в движке Vim связаны с его наследством от vi. Одним конкретным ограничением являются группы захвата, где вы ограничены 9 из них, и нет никакого способа обойти это.
Учитывая это ограничение, вы обнаружите, что группы захвата обычно используются реже (и, когда используются они менее мощные), чем в Python.
Один из вариантов, который следует учитывать, - это использовать Python в Vim вместо Vimscript. Хотя обычно это влияет на переносимость, поэтому лично я бы не стал переключаться только на эту функцию.
Есть ли более или менее простой способ сделать это в чистом Vimscript, тогда?
Один из вариантов - переопределить версию split()
, которая сохраняет разделители, используя matchstrpos()
. Например:
function! SplitDelim(expr, pat)
let result = []
let expr = a:expr
while 1
let [w, s, e] = matchstrpos(expr, a:pat)
if s == -1
break
endif
call add(result, s ? expr[:s-1] : '')
call add(result, w)
let expr = expr[e:]
endwhile
call add(result, expr)
return result
endfunction