Ваш регекс схильний до вторгнення в деяку поведінку O (N ^ 2) механізму зворотного відстеження, який використовується у Vim (та багатьох інших мовах та середовищах).
На щастя, є способи написання еквівалентних виразів, які не викликають зайвого зворотного відстеження. Наприклад:
/^\([^|]*|\)\{8}.*$
Загалом, вам не потрібно відповідати "вісім і більше", оскільки якщо ви вже знаєте, рядок є проблематичним, якщо він має вісім (чи є їх більше, чи ні).
Якщо вам потрібно зіставити весь рядок (наприклад, тому що це частина :s
операції), вам потрібно буде зберегти останню частину ( .*$
); якщо ви просто використовуєте регулярний вираз, щоб знайти вісім рядків або більше, тоді ви можете залишити .*$
кінець.
Також я раджу лише намагатися відповідати одній «стороні» труби всередині групи, яку ви повторите. Це спрощує як мислення про те, як регулярний вирівнювання відповідає рядкам, так і як сам механізм регулярного виведення виконує (це виключає джерело зворотного відстеження).
Тепер, щоб пояснити трохи про "зворотний трек". Подумайте, у вас є лінія, яка містить вісім символів труби:
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
Наступний уривок описує, як механізм регулярного вирівнювання намагається співставити ваше вираження з вищевказаним рядком (я додав додаткове пробіл до рядків регулярних виразів, щоб показати (приблизно), де частини регулярного вираження відповідають символам самого рядка).
Перший .*
жадібний і відповідатиме всім до кінця рядка, залишаючи характер труби незмінним.
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |
Найновіший "стискається" матч відмовляється від біту своєї відповідності та повторює спробу решти регексу. У цьому випадку це відбувається по одному символу одночасно (оскільки .
буде відповідати будь-якому одному символу). Це зворотне відстеження триває до тих пір, поки інший вираз не зможе збігатися (або поки він не повернеться до початку - це єдиний спосіб, коли він знає, що рядок не відповідає виразу!).
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|
Отже, перший .*
відступив достатньо, щоб залишити решту групи, але для другої групи нічого не було. Час відступити ще.
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|
Зворотний трек знайшов нову точку зупинки, але тепер друга .*
в першій групі проводить свою жадібну відповідність. Друга група не відповідає. .*
Починається зворотний трек у другої групи.
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.*)(.*|.* )(.*|
Друга група знайшла збіг, але третя група не відповідала. Знову зворотний трек, починаючи з останнього матчу. Друга .*
з другої групи відступає назад ні до чого. Перший .*
з другої групи відступає ні до чого. Друга .*
з першої групи відступає ні до чого. Перший .*
з перших групових успішно відкликав.
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|
Але знову ж таки, другий .*
жадібний, тому для другої групи він нічого не залишає.
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|.* )(.*|
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.*)(.*|.*)(.*|.* )(.*|
Врешті-решт всі три групи відповідають, але четвертий примірник групи виходить з ладу. Почніть зворотній трек.
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|.* )(.*|
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.* )(.*|.*)(.*|.* )(.*|
aaaaaa|bbbbbb|cccccc|dddddd|eeeeee|ffffff| gg | gg |hhhhhh
^(.* |.*)(.*|.*)(.*|.*)(.*|.* )(.*|
Ви можете бачити, як це спалює багато часу (діаграми навіть пропускають перегляд символів за символами, що відбувається насправді; вище показані лише "високі точки"). Проблема випливає з того, що попередній шматочок регексу жадібно збігається з тим, що пізніше частина регулярного виразів зрештою повинна буде відповідати, щоб отримати належну кількість повторень групи.
На мій вираз, кожне повторення ( [^|]*
) ніколи не відповідає тому, що відповідає наступному елементу ( |
), тому зворотний трек є чисто лінійним. Як тільки починається зворотний трекінг для кожного "стискаючого" матчу, він (за лінійним часом) виявить, що немає більш ранніх місць, де може відповідати наступний вираз; це змушує зворотний трек продовжувати попередній "скорочувальний" матч, поки нічого не збігається, і вся лінія не буде вирішена невідповідно.
Замість "нуля чи більше нетрубних, то труба" ( [^|]*|
), також можна використовувати .
з явно не жадібним повторенням ( \{-}
у Vim, але воно змінюється; використовуються інші мови регулярного виведення *?
).
^\(.\{-}|\)\{8}.*$