Поради щодо Regex Golf


43

Подібно до наших тем, що стосуються мовних порад щодо гольфу: які загальні хитрощі для скорочення регулярних виразів?

Я можу побачити три способи використання регулярного вираження, коли мова йде про гольф: класичний регекс-гольф ("ось список, який повинен відповідати, і ось список, який повинен вийти з ладу"), використовуючи регулярний вираз для вирішення обчислювальних задач і регулярних виразів, використовуваних як частини більший код для гольфу. Не соромтеся розміщувати поради, що стосуються будь-якого чи всього цього. Якщо ваша порада обмежена одним або кількома ароматами, будь ласка, вкажіть ці ароматизатори вгорі.

Як завжди, будь ласка, дотримуйтесь однієї підказки (або сімейства дуже споріднених порад) за кожну відповідь, щоб найбільш корисні поради піднялися до вершини голосуванням.


Відверте самореклама: до якої категорії використання регулярних викидів це підпадає? codegolf.stackexchange.com/a/37685/8048
Кайл Странд

@KyleStrand "регулярні вирази, які використовуються як частини більшого коду для гольфу".
Мартін Ендер

Відповіді:


24

Коли не втекти

Ці правила стосуються більшості ароматів, якщо не всіх:

  • ] не потребує втечі, коли не має аналогів.

  • {і }не потрібно бігти, коли вони не є частиною повторення, наприклад, {a}матчі {a}буквально. Навіть якщо ви хочете відповідати чомусь подібному {2}, вам потрібно уникнути лише одного з них, наприклад {2\}.

У класах персонажів:

  • ]не потрібно бігти, коли це перший символ у наборі символів, наприклад, []abc]відповідає одному з ]abcабо коли він є другим символом після ^, наприклад, [^]]відповідає нічого, але ]. (Помітний виняток: аромат ECMAScript!)

  • [взагалі не потрібно бігти. Разом з вищевказаною підказкою це означає, що ви можете зіставити обидва дужки з жахливо контр-інтуїтивним класом персонажів [][].

  • ^не потрібно бігти, коли це не перший символ у наборі символів, наприклад [ab^c].

  • -не потрібно бігти, коли це або перший (другий після а ^), або останній символ у наборі символів, наприклад [-abc], [^-abc]або [abc-].

  • Жодним іншим символам не потрібно бігти всередині класу символів, навіть якщо вони є мета-символами поза класами символів (за винятком \самого зворотного косого кута).

Крім того , в деяких смаки ^і $підібрані в буквальному сенсі , коли вони не перебувають на початку або в кінці регулярного виразу відповідно.

(Дякуємо @ MartinBüttner за заповнення кількох деталей)


Деякі вважають за краще уникнути фактичної точки, включивши її в клас символів, де не потрібно уникати (наприклад, [.]). Якщо \.
уникнути

Зауважте, що [необхідно уникати на Java. Не впевнений у ICU (використовується в Android та iOS) або .NET.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

18

Простий регулярний вираз, який відповідає всім символам для друку в таблиці ASCII .

[ -~]

1
чиста дивовижність, всі ознаки зі стандартної американської клавіатури! Примітка: стандартна таблиця ascii (не враховуючи розширеного діапазону 127-255
CSᵠ

Я використовую його часто, але у нього відсутній загальний "регулярний" символ: TAB. І передбачається, що ви використовуєте LC_ALL = "C" (або подібне), оскільки деякі інші локалі вийдуть з ладу.
Олів'є Дулак

Чи може дефіс використовуватися таким чином, щоб вказати будь-який діапазон символів у таблиці ASCII? Це працює на всі аромати регексу?
Джош Вітхі

14

Знайте свої смакові рецепти

Є дивовижна кількість людей, які вважають, що регулярні вирази є по суті мовними агностиками. Однак насправді існують досить істотні відмінності між смаками, і особливо для кодового гольфу добре знати декілька з них та їх цікаві особливості, так що ви можете підібрати найкраще для кожного завдання. Ось огляд кількох важливих ароматів та того, що відрізняє їх від інших. (Цей список насправді не може бути повним, але дайте мені знати, якщо я пропустив щось дійсно кричуще.)

Perl і PCRE

Я кидаю їх в один горщик, оскільки я не надто знайомий зі смаком Perl, і вони здебільшого еквівалентні (PCRE - це все-таки Perl-сумісні регулярні вирази). Основна перевага аромату Perl полягає в тому, що ви можете дійсно зателефонувати за кодом Perl зсередини регулярного вираження та заміни.

  • Рекурсія / підпрограми . Мабуть, найважливіша особливість для гольфу (яка існує лише в парі ароматів).
  • Умовні візерунки (?(group)yes|no).
  • Підтримує зміна в разі заміни рядка з \l, \u, \Lі \U.
  • PCRE дозволяє чергуватись у відстані, де кожна альтернатива може мати різну (але фіксовану) довжину. (Більшість ароматів, включаючи Perl, вимагають, щоб виглядники мали загальну фіксовану довжину.)
  • \G прив’язати відповідність до кінця попереднього матчу.
  • \K щоб скинути початок матчу
  • PCRE підтримує як властивості символів Unicode, так і сценарії .
  • \Q...\Eщоб уникнути більш тривалих символів. Корисно, коли ви намагаєтеся зіставити рядок, що містить багато мета-символів.

.NET

Це, мабуть, найпотужніший аромат, із недостатками дуже мало.

Одним з важливих недоліків у плані гольфу є те, що він не підтримує властивих кванторів, як деякі інші аромати. Замість цього .?+вам доведеться писати (?>.?).

Java

  • Через помилку (див. Додаток) Java підтримує обмежений тип змінної довжини ззаду: ви можете дивитися позаду до початку рядка, .*звідки тепер ви можете запустити пошук, наприклад (?<=(?=lookahead).*).
  • Підтримує об'єднання та перетин класів символів.
  • Має найбільш широку підтримку Unicode, з класами символів для "Сценаріїв, блоків, категорій та бінарних властивостей Unicode" .
  • \Q...\E як у Perl / PCRE.

Рубін

В останніх версіях цей аромат аналогічно потужний як PCRE, включаючи підтримку викликів підпрограми. Як і Java, він також підтримує об'єднання та перетин класів символів. Однією особливістю є вбудований клас символів для шістнадцяткових цифр: \h(та заперечення \H).

Найбільш корисною особливістю для гольфу є те, як Рубі обробляє квантори. Найбільш помітно, що можна встановити квантори без дужок. .{5,7}+працює і так робить .{3}?. Крім того, на відміну від більшості інших ароматизаторів, якщо нижня межа квантора 0може бути опущена, наприклад .{,5}, еквівалентна .{0,5}.

Що стосується підпрограм, основна відмінність між підпрограмами PCRE і підпрограмами Рубі полягає в тому, що синтаксис Рубі є байтом довше (?n)проти \g<n>, але підпрограми Рубі можна використовувати для захоплення, тоді як PCRE скидає зйомки після закінчення підпрограми.

Нарешті, Ruby має іншу семантику для лінійних модифікаторів, ніж більшість інших ароматів. У Ruby завжди ввімкнено модифікатор, який зазвичай називають mіншими ароматами . Отже, і завжди порівнюйте початок і кінець рядка, а не лише початок і кінець рядка. Це може заощадити вам байт, якщо вам потрібна така поведінка, але це коштуватиме додаткових байтів, якщо ви цього не зробите, тому що вам доведеться замінити і на, і відповідно. На додаток до цього, модифікатор, який зазвичай називається (який робить підсилювач ліній передач), називається в Ruby. Це не впливає на кількість байтів, але слід пам’ятати, щоб уникнути плутанини.^$^$\A\zs.m

Пітон

Python має міцний аромат, але я не знаю жодних особливо корисних функцій, яких ви б не знайшли більше ніде.

Однак є альтернативний аромат, який призначений замінити reмодуль в якийсь момент і який містить багато цікавих особливостей. Окрім додавання підтримки для рекурсії, огляду операторів комбінації класів символів із змінною довжиною, вона також має унікальну особливість нечіткого зіставлення . По суті, ви можете вказати ряд помилок (вставки, видалення, заміни), які допускаються, і двигун також дасть вам приблизні збіги.

ECMAScript

Аромат ECMAScript дуже обмежений, а отже, рідко дуже корисний для гри в гольф. Єдине, для чого це дістається - це заперечений порожній символ символів [^] для відповідності будь-якому символу, а також безумовно невдалий клас порожніх символів [](на відміну від звичайного (?!)). На жаль, аромат не має ніяких особливостей, що робить останній корисним для нормальних проблем.

Луа

У Lua є свій досить неповторний аромат, який досить обмежений (наприклад, ви навіть не можете кількісно оцінити групи), але він має декілька корисних та цікавих особливостей.

  • У ньому є велика кількість скорочень для вбудованих класів символів , включаючи розділові знаки, великі та малі символи та шістнадцяткові цифри.
  • З %bним підтримується дуже компактний синтаксис, який відповідає збалансованим рядкам. Наприклад, %b()відповідність а, (а потім все до відповідності )(правильно пропускаючи внутрішні пари). (і тут )можуть бути будь-які два символи.

Підвищення

Регекс-аромат Boost по суті є перламутровим. Однак він має деякі приємні нові функції заміни регулярних виразів, включаючи зміни в регістрі та умовні умови . Останнє є унікальним для Boost, наскільки я знаю.


Зауважте, що погляд вперед у огляді буде пробиватися через обмежену межу в позаду. Тестовано на Java та PCRE.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Це не .?+рівнозначно .*?
CalculatorFeline

@CalculatorFeline Перший - це потужний кількісний коефіцієнт 0 або 1 (в ароматах, які підтримують присвійні квантори), останній - кількісний показник 0 або більше.
Мартін Ендер

@CalculatorFeline ах я розумію плутанину. Був друкарський помилок.
Мартін Ендер

13

Знайте свої класи персонажів

Більшість ароматів регулярного вираження мають заздалегідь визначені класи персонажів. Наприклад, \dвідповідає десятковій цифрі, яка на три байти коротша за [0-9]. Так, вони можуть дещо відрізнятися, оскільки вони \dможуть також відповідати цифрам Unicode, а також у деяких смаках, але для більшості проблем це не змінить.

Ось кілька класів персонажів, які можна знайти у більшості ароматів регулярних виразів:

\d      Match a decimal digit character
\s      Match a whitespace character
\w      Match a word character (typically [a-zA-Z0-9_])

Крім того, ми також маємо:

\D \S \W

які заперечують версії вищезазначеного.

Не забудьте перевірити свій смак на наявність додаткових класів символів. Наприклад, PCRE має \Rнові рядки, а Lua навіть має такі класи, як рядкові та великі літери.

(Дякую @HamZa та @ MartinBüttner за те, що вони вказали на них)


3
\Rдля нових рядків у PCRE.
HamZa

12

Не турбуйтеся з групами, які не захоплюють (якщо ...)

Ця порада стосується (принаймні) всіх популярних ароматів Perl.

Це може бути очевидним, але (коли це не гольф), добре використовувати групи, що не захоплюють, (?:...)коли це можливо. Ці два додаткових персонажа ?:марно витрачаються під час гри в гольф, тому просто використовуйте групи захоплення, навіть якщо ви не збираєтесь їх повторювати.

Хоча є одне (рідкісне) виняток: якщо трапляється група із зворотною референцією 10принаймні 3 рази, ви можете насправді зберегти байти, перетворивши попередню групу в групу, яка не захоплює, так що всі ці \10стають \9s. (Подібні трюки застосовуються, якщо ви використовуєте групу 11принаймні 5 разів тощо).


Чому 11 потрібно 5 разів, щоб бути вартим, коли 10 вимагає 3?
Нік Хартлі

1
@QPaysTaxes може використовувати $9замість одного разу $10або $11зберігає один байт. Для $10перетворення $9потрібен один ?:, що становить два байти, тож вам потрібно три $10s, щоб щось зберегти. Для $11перетворення $9потрібні два ?:s, що становить чотири байти, тож вам потрібно буде п’ять $11s, щоб зберегти щось (або п’ять $10і $11комбіновано).
Мартін Ендер

10

Рекурсія для повторного використання візерунка

Жменька ароматів підтримує рекурсію (наскільки мені відомо , Perl, PCRE та Ruby). Навіть коли ви не намагаєтеся вирішити рекурсивні проблеми, ця функція може зберегти багато байт у складніших шаблонах. Немає необхідності здійснювати дзвінок до іншої (іменованої чи нумерованої) групи всередині цієї групи. Якщо у вас є певний зразок, який з’являється кілька разів у вашому регулярному виразі, просто згрупуйте його та зверніться до нього поза цією групою. Це не відрізняється від виклику підпрограми у звичайних мовах програмування. Тож замість

...someComplexPatternHere...someComplexPatternHere...someComplexPatternHere... 

в Perl / PCRE ви можете:

...(someComplexPatternHere)...(?1)...(?1)...

або в Ruby:

...(someComplexPatternHere)...\g<1>...\g<1>...

за умови, що це перша група (звичайно, ви можете використовувати будь-яке число в рекурсивному дзвінку).

Зверніть увагу, що це не те саме, що зворотний зв'язок ( \1). Зворотні параметри відповідають точно такому ж рядку, який група відповідала останній раз. Ці виклики підпрограми фактично знову оцінюють шаблон. Як приклад someComplexPatternHereвзяти тривалий клас символів:

a[0_B!$]b[0_B!$]c[0_B!$]d

Це відповідало б чомусь подібному

aBb0c!d

Зауважте, що ви не можете використовувати зворотні посилання тут, зберігаючи поведінку. Зворотній посилання зазнає невдачі на рядку вище, так Bі 0і !не те ж саме. Однак при викликах підпрограми шаблон фактично переоцінюється. Наведена вище модель повністю рівнозначна

a([0_B!$])b(?1)c(?1)d

Захоплення у викликах підпрограми

Одне застереження для Perl та PCRE: якщо група 1у наведених вище прикладах містить інші групи, то виклики підпрограми не запам’ятають їх захоплення. Розглянемо цей приклад:

(\w(\d):)\2 (?1)\2 (?1)\2

Це не відповідатиме

x1:1 y2:2 z3:3

оскільки після повернення викликів підпрограми новий захоплення групи 2відкидається. Натомість цей шаблон відповідає цій рядку:

x1:1 y2:1 z3:1

Це відрізняється від Ruby, де виклики підпрограм робити зберігають свої знімки, так що еквівалентно Рубіновий регулярний вираз (\w(\d):)\2 \g<1>\2 \g<1>\2буде відповідати першому з наведених вище прикладів.


Ви можете використовувати \1для Javascript. І PHP теж (я думаю).
Ісмаїл Мігель

5
@IsmaelMiguel Це не зворотний зв'язок. Це фактично знову оцінює шаблон. Наприклад, (..)\1буде збігатися, ababале не вдасться, abbaтоді як (..)(?1)відповідатиме останньому. Це насправді виклик підпрограми в тому сенсі, що вираз застосовується знову, замість того, щоб буквально збігатися з тим, що воно відповідало минулого разу.
Мартін Ендер

Нічого собі, я поняття не мав! Навчання чомусь новому
Ісмаель Мігель

У .NET (або інших ароматах без цієї функції):(?=a.b.c)(.[0_B!$]){3}d
jimmy23013

@ user23013, що здається дуже специфічним для цього конкретного прикладу. Я не впевнений, що це застосовно, якщо я повторно використовую певний субфорт у різних округах.
Мартін Ендер

9

Причиняється збій матчу

Використовуючи регулярний вираз для вирішення обчислювальних задач або співставлення дуже нерегулярних мов, іноді необхідно зробити гілку шаблону невдалою, незалежно від того, де ви знаходитесь у рядку. Наївний підхід полягає у використанні порожньої негативної підказки:

(?!)

Вміст (порожній шаблон) завжди збігається, тому негативний пошук завжди виходить з ладу. Але частіше за все є набагато простіший варіант: просто використовуйте символ, який ви знаєте, ніколи не з’явиться у вводі. Наприклад, якщо ви знаєте, що ваш внесок завжди буде складатися лише з цифр, ви можете просто використовувати

!

або будь-який інший нецифровий неметальний символ, який може спричинити збій.

Навіть якщо ваш вклад потенційно може містити будь-які підрядки, існують більш короткі способи, ніж (?!). Будь-який аромат, який дозволяє якорям з’являтися в межах візерунка, на відміну від кінця, може використовувати будь-яке з наступних 2-символьних рішень:

a^
$a

Зауважте, однак, що деякі аромати будуть сприйматись ^і $як буквальні символи в цих позиціях, оскільки вони, очевидно, насправді не мають сенсу як якоря.

В ароматі ECMAScript також є досить елегантне 2-символьне рішення

[]

Це порожній клас символів, який намагається переконатися, що наступні символи є одним із тих у класі - але в класі немає символів, тому це завжди не вдається. Зауважте, що це не спрацює в будь-якому іншому ароматі, оскільки класи символів зазвичай не можуть бути порожніми.


8

Оптимізуйте свої АБО

Щоразу, коли у вашому RegEx є 3 або більше альтернативи:

/aliceblue|antiquewhite|aquamarine|azure/

Перевірте, чи є загальний початок:

/a(liceblue|ntiquewhite|quamarine|zure)/

А може навіть спільне закінчення?

/a(liceblu|ntiquewhit|quamarin|zur)e/

Примітка: 3 - це лише початок, і він би враховував однакову довжину, 4+ може змінити значення


Але що робити, якщо не всі вони мають спільний префікс? (пробіл додано лише для наочності)

/aliceblue|antiquewhite|aqua|aquamarine|azure
|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood
|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan/

Згрупуйте їх до тих пір, поки правило 3+ має сенс:

/a(liceblue|ntiquewhite|qua|quamarine|zure)
|b(eige|isque|lack|lanchedalmond|lue|lueviolet|rown|urlywood)
|c(adetblue|hartreuse|hocolate|oral|ornflowerblue|ornsilk|rimson|yan)/

Або навіть узагальнити, якщо ентропія задовольняє вашому користувальницькому шафу:

/\w(liceblue|ntiquewhite|qua|quamarine|zure
|eige|isque|lack|lanchedalmond|lue|lueviolet|rown|urlywood
|adetblue|hartreuse|hocolate|oral|ornflowerblue|ornsilk|rimson|yan)/

^ в цьому випадку ми впевнені, що ми не отримаємо жодного clueабоcrown slack Ryan

Це "згідно з деякими тестами" також покращує продуктивність, оскільки забезпечує якір для запуску.


1
Якщо загальний початок або кінець довший одного символу, навіть групування двох може змінити значення. Як aqua|aquamarineaqua(|marine)або aqua(marine)?.
Paŭlo Ebermann

6

Цей досить простий, але варто зазначити:

Якщо ви повторили клас символів, [a-zA-Z]ви, ймовірно, просто можете скористатись [a-z]і додатиi ( case- i nsensitive модифікатор) до свого регулярного вираження.

Наприклад, у Ruby такі два регулярні вирази рівнозначні:

/[a-zA-Z]+\d{3}[a-zA-Z]+/
/[a-z]+\d{3}[a-z]/i - на 7 байт коротше

З цього питання, інші модифікатори також можуть скоротити вашу загальну довжину. Замість цього:

/(.|\n)/

який відповідає БУДЬ-якому символу (оскільки крапка не відповідає новому рядку ), використовуйте модифікатор s ingle-lines , що робить крапку у відповідності з новими рядками.

/./s - 3 байти коротше


У Ruby є тонна вбудованих класів символів для регулярного виведення. Дивіться цю сторінку та шукайте "Властивості символів".
Прекрасний приклад - «Символ валюти». Згідно з Вікіпедією, існує безліч можливих символів валюти, і розмістити їх у класі символів було б дуже дорого ( [$฿¢₡Ð₫€.....]), тоді як ви можете зіставити будь-який з них у 6 байт:\p{Sc}


1
За винятком JavaScript, де sмодифікатор не підтримується. :( Але там ви можете скористатися фірмовою /[^]/хитрістю JavaScript .
manatwork

Зауважте, що (.|\n)це не працює навіть у деяких ароматах, оскільки .часто також не відповідає іншим типам роздільників ліній. Однак звичайним способом зробити це (без s) [\s\S]є той самий байт, що і (.|\n).
Мартін Ендер

@ MartinBüttner, моя ідея полягала в тому, щоб зберегти його разом з іншим рядком, що закінчується пов'язаними порадами. Але якщо ви вважаєте, що ця відповідь стосується більше модифікаторів, я не заперечую, якщо ви її перекладете.
манатура

@manatwork зроблено (і додав також пов'язаний фокус, що не стосується ES)
Мартін Ендер

6

Простий мовний аналізатор

Ви можете побудувати дуже простий аналізатор з RE подібним \d+|\w+|".*?"|\n|\S. Маркери, з якими потрібно зіставити, відокремлюються символом RE 'або'.

Кожен раз, коли двигун RE намагається співставити поточну позицію в тексті, він спробує перший шаблон, потім другий і т. Д. Якщо він не працює (наприклад, пробіл тут, наприклад), він рухається далі і намагається знову відповідати . Порядок важливий. Якщо ми поставили цей \Sтермін перед \d+терміном, то\S би спочатку відповідав будь-якому символу, який не є пробілом, який би порушив наш аналіз.

".*?"Рядок узгодження використовує нежадібні модифікатор , тому ми тільки відповідати одному рядку за раз. Якщо ваш RE не має не жадібних функцій, ви можете скористатись "[^"]*"еквівалентом.

Приклад Python:

text = 'd="dogfinder"\nx=sum(ord(c)*872 for c in "fish"+d[3:])'
pat = r'\d+|\w+|".*?"|\n|\S'
print re.findall(pat, text)

['d', '=', '"dogfinder"', '\n', 'x', '=', 'sum', '(', 'ord', '(', 'c', ')',
    '*', '872', 'for', 'c', 'in', '"fish"', '+', 'd', '[', '3', ':', ']', ')']

Приклад гольф-пітона:

# assume we have language text in A, and a token processing function P
map(P,findall(r'\d+|\w+|".*?"|\n|\S',A))

Ви можете налаштувати шаблони та їх порядок відповідно до потрібної мови. Ця методика добре працює для JSON, базових HTML та числових виразів. Він багато разів успішно використовувався з Python 2, але повинен бути досить загальним для роботи в інших середовищах.


6

\K замість позитивного погляду позаду

PCRE і Perl підтримують послідовність втечі \K, яка скидає початок матчу. Тобто ab\Kcdбуде потрібно, щоб ваш вхідний рядок містив, abcdале повідомлення про відповідність буде тільки cd.

Якщо ви використовуєте позитивний погляд позаду на початку шаблону (який, мабуть, найімовірніше місце), то в більшості випадків ви можете використовувати \Kзамість цього і зберегти 3 байти:

(?<=abc)def
abc\Kdef

Це рівнозначно для більшості цілей, але не повністю. Відмінності приносять і переваги, і недоліки:

  • Вгору: PCRE та Perl не підтримують довільну довжину (лише для .NET). Тобто, ти не можеш робити щось подібне (?<=ab*). Але \Kви можете поставити перед ним будь-який візерунок! Так ab*\Kпрацює. Це фактично робить цю техніку набагато більш потужною у випадках, коли вона застосовна.
  • Перевернута сторона: значки навколо не відступають. Це актуально, якщо ви хочете пізніше зробити щось у погляді позаду, але є кілька можливих захоплень, які призводять до дійсних збігів. У цьому випадку двигун регулярного випробовування коли-небудь спробував би одну з цих можливостей. При використанні\K цієї частини регексу відтворюється, як і все інше.
  • Знизу: Як ви, напевно, знаєте, кілька збігів регулярного вираження не можуть перетинатися. Часто lookarounds використовуються для часткового подолання цього обмеження, оскільки lookahead може підтвердити частину рядка, яка вже була використана попереднім збігом. Так що якщо ви хочете , щоб всі символи , які потім ab можна використовувати (?<=ab).. З огляду на вхід

    ababc
    

    це відповідало б другому aта c. Це неможливо відтворити за допомогою \K. Якби ви раніше ab\K., ви отримаєте лише перший матч, тому що зараз abце не в оточенні.


Якщо шаблон використовує \Kпослідовність евакуації в позитивному твердженні, повідомлений початок успішного матчу може бути більшим, ніж кінець матчу.
hwnd

@hwnd Моя точка зору, що , з огляду на ababc, що немає ніякого способу , щоб відповідати як другий aі тому cз \K. Ви отримаєте лише одну відповідність.
Мартін Ендер

Ви маєте рацію, не з самою функцією. Ви б якір з\G
HWND

@hwnd Ах, зараз я бачу вашу думку. Але я гадаю, що в цей момент (з точки зору гольфу) вам краще відмовитися від негативного погляду, тому що вам насправді це навіть може знадобитися, оскільки ви не можете бути впевнені, що .останній матч був насправді a.
Мартін Ендер

1
Цікаве використання \ K =)
hwnd

5

Відповідність будь-якого персонажа

В ароматі ECMAScript бракує sмодифікаторів, що .відповідає будь-якому символу (включаючи нові рядки). Це означає, що не існує односимвольного рішення для відповідності повністю довільним символам. Стандартне рішення в інших ароматах (коли хтось не хоче використовувати sз якоїсь причини) є [\s\S]. Однак, ECMAScript єдиний смак (до мого знання) , який підтримує порожні класи символів, і , отже , має набагато більш короткий варіант: [^]. Це заперечений клас порожніх символів - тобто він відповідає будь-якому символу.

Навіть для інших ароматів ми можемо навчитися цій техніці: якщо ми не хочемо використовувати s(наприклад, тому, що нам все-таки потрібно звичне значення .в інших місцях), все одно може бути коротший спосіб зіставити як символи нового рядка, так і друковані, за умови, що якийсь символ, який ми знаємо, не відображається на вході. Скажімо, ми обробляємо номери, обмежені новими рядками. Тоді ми можемо зіставити будь-який символ [^!], оскільки ми знаємо, що !він ніколи не буде частиною рядка. Це економить два байти над наївним [\s\S]або [\d\n].


4
У Perl \Nозначає саме те, що .означає поза /sрежимом, за винятком того, що режим не впливає на нього.
Конрад Боровський

4

Використовуйте атомні групи та присвійні квантори

Я знайшов атомні групи ( (?>...)) і присвійні квантори ( ?+, *+, ++,{m,n}+ ) іноді дуже корисно для гри в гольф. Він відповідає рядку і забороняє відкликати пізніше. Таким чином, він буде відповідати лише першій збіжній рядку, яку знайде движок регулярних виразів.

Наприклад: Щоб відповідати рядок з непарною кількістю a's на початку, за яким не слідує більше a' s, ви можете використовувати:

^(aa)*+a
^(?>(aa)*)a

Це дозволяє використовувати такі речі .* вільно, і якщо є очевидна відповідність, не буде іншої можливості збігати занадто багато або занадто мало символів, що може порушити ваш візерунок.

У регулярному вираженні .NET (який не має присвоєних кількісних показників), ви можете використовувати це для поп-групи 1, найбільший кратний з 3 (максимум 30) разів (не дуже гольф):

(?>((?<-1>){3}|){10})

1
У ECMAскрипті також відсутні відсутні присвійні кількісні показники або атомні групи :(
CSᵠ

4

Забудьте про захоплену групу після субекспресії (PCRE)

Для цього регулярного вираження:

^((a)(?=\2))(?!\2)

Якщо ви хочете очистити \ 2 після групи 1, ви можете використовувати рекурсію:

^((a)(?=\2)){0}(?1)(?!\2)

Він збігатиметься, aaпоки попередній не буде. Іноді ви також можете використовувати ??або навіть ?замість них {0}.

Це може бути корисно, якщо ви багато використовували рекурсії, а деякі зворотні референції або умовні групи з’явились у різних місцях вашого регексу.

Також зауважте, що атомні групи передбачаються для рекурсій в PCRE. Отже, це не збігатиметься з однією буквою a:

^(a?){0}(?1)a

Я ще не пробував цього в інших ароматах.

Для цілей пошуку ви також можете використовувати подвійні негативи для цієї мети:

^(?!(?!(a)(?=\1))).(?!\1)

4

Необов’язкові вирази

Іноді це корисно пам’ятати

(abc)?

в основному такий же , як

(abc|)

Однак є невелика різниця: у першому випадку група або захоплює, abcабо взагалі не фіксує. Останній випадок призведе до відмови зворотного зв'язку. У другому виразі група буде або захоплювати, abcабо порожній рядок, де в останньому випадку безвідмовно збігатиметься зворотний зв'язок. Щоб наслідувати останню поведінку, ?вам потрібно оточити все в іншій групі, що коштуватиме два байти:

((abc)?)

Використовувана версія |також корисна, коли ви хочете все-таки обернути вираз у будь-якій іншій формі групи і не піклуватися про захоплення:

(?=(abc)?)
(?=abc|)

(?>(abc)?)
(?>abc|)

Нарешті, цей трюк може бути застосований і до ungreedy, ?коли він зберігає байт навіть у сирому вигляді (і, отже, 3 байти у поєднанні з іншими формами груп):

(abc)??
(|abc)

1

Кілька локомотивів, які завжди відповідають (.NET)

Якщо у вас є 3 або більше конструкцій lookahead, які завжди збігаються (для зйомки піддепресій), або на кількісному оцінці є кількісний коефіцієнт, який супроводжується чимось іншим, тому вони повинні бути в не обов'язково захопленій групі:

(?=a)(?=b)(?=c)
((?=a)b){...}

Вони коротші:

(?(?(?(a)b)c))
(?(a)b){...}

де aне повинно бути назви захопленої групи. Ви не можете використовувати |для позначення звичайної речі в bіc без додавання іншої пари в дужках.

На жаль, врівноважувальні групи в умовному режимі здавалися непосильними, що робить його марним у багатьох випадках.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.