Є популярна цитата Джеймі Завінського :
Деякі люди, стикаючись з проблемою, думають, "я знаю, я буду використовувати регулярні вирази". Зараз у них дві проблеми.
Як слід розуміти цю цитату?
Є популярна цитата Джеймі Завінського :
Деякі люди, стикаючись з проблемою, думають, "я знаю, я буду використовувати регулярні вирази". Зараз у них дві проблеми.
Як слід розуміти цю цитату?
Відповіді:
Деякі технології програмування, як правило, не розуміються програмістами ( регулярні вирази , плаваюча точка , Perl , AWK , IoC ... та інші ).
Це можуть бути дивовижно потужні інструменти для вирішення правильного набору проблем. Зокрема, регулярні вирази дуже корисні для відповідності звичайним мовам. І в цьому суть проблеми: мало хто знає, як описати звичайну мову (це частина теорії інформатики / лінгвістики, яка використовує кумедні символи - про це можна прочитати в ієрархії Хомського ).
Якщо ви маєте справу з цими речами, якщо ви їх неправильно використовуєте, навряд чи ви вирішили свою первісну проблему. Використання регулярних виразів для відповідності HTML (далеко надто поширене явище) означатиме , що ви будете пропустити крайні випадки. А тепер у вас все ще є оригінальна проблема, яку ви не вирішили, і ще одна непомітна помилка, що обертається навколо, що була введена за допомогою неправильного рішення.
Це не означає, що регулярні вирази не повинні використовуватися, а скоріше, щоб працювати над розумінням того, який набір проблем вони можуть вирішити, а не можуть їх вирішити та використовувати їх розумно.
Запорукою підтримки програмного забезпечення є написання коду, що підтримується. Використання регулярних виразів може суперечити цій меті. Працюючи з регулярними виразами, ви написали міні-комп’ютер (зокрема, недетермінований автомат з кінцевими станами ) спеціальною мовою, специфічною для домену. Неважко написати цією мовою еквівалент "Hello world" та завоювати до цього рудиментарну впевненість, але далі потрібно гартувати розуміння звичайної мови, щоб уникнути написання додаткових помилок, які важко визначити та виправити (адже вони не є частиною програми, в якій є регулярний вираз).
Отже, у вас з’явилася нова проблема; ви вибрали інструмент регулярного вираження для його вирішення (коли це недоречно), і у вас зараз є дві помилки, яких обидва важче знайти, оскільки вони заховані в іншому шарі абстракції.
Регулярні вирази - особливо нетривіальні - потенційно важко кодувати, розуміти та підтримувати. Потрібно лише переглянути кількість запитань на тезі Stack Overflow, [regex]
де запитуючий припустив, що відповідь на їх проблему - це регулярний вираз і згодом застряг. У багатьох випадках проблему можна (а можливо, і слід) вирішити по-іншому.
Це означає, що якщо ви вирішили використовувати регулярний вираз, у вас виникли дві проблеми:
В основному, я думаю, що він означає, що ви повинні використовувати регулярний вираз, тільки якщо немає іншого способу вирішення вашої проблеми. Можливо, іншим рішенням буде простіше кодувати, підтримувати та підтримувати. Це може бути повільніше або менш ефективним, але якщо це не критичне простота обслуговування та підтримки, це має бути головним питанням.
Це переважно жартівливий жарт, хоч із зерном правди.
Є кілька завдань, для яких регулярні вирази - прекрасна відповідність. Одного разу я замінив 500 рядків рукописного рекурсивного коду синтаксичного аналізатора на один регулярний вираз, на повне налагодження якого пішло близько 10 хвилин. Люди кажуть, що регулярні вирівнювання важко зрозуміти та налагодити, але належним чином застосувати їх не так вже й складно, як і величезний розроблений вручну аналізатор. У моєму прикладі знадобилося два тижні, щоб налагодити всі крайні випадки рішення, що не піддається повторному виведенню.
Однак перефразовуючи дядька Бена:
З великою виразністю настає велика відповідальність.
Іншими словами, регулярні вирази додають виразності вашій мові, але це покладає більше відповідальності на програміста за вибір найбільш читаного способу вираження для даної задачі.
Деякі речі спочатку виглядають як гарне завдання для регулярних виразів, але ні. Наприклад, що завгодно з вкладеними маркерами, як-от HTML. Іноді люди використовують регулярний вираз, коли більш простий метод більш зрозумілий. Наприклад, string.endsWith("ing")
простіше зрозуміти, ніж еквівалентний регулярний вираз. Іноді люди намагаються врізати велику проблему в єдиний регулярний вираз, де доцільніше розбити його на частини. Іноді людям не вдається створити відповідні абстракції, повторюючи регулярний вираз замість того, щоб створити добре названу функцію, щоб виконувати ту саму роботу (можливо, реалізовану внутрішньо за допомогою регулярного вираження).
З певних причин регекси мають дивну тенденцію до створення сліпої плями до нормальних принципів інженерії програмного забезпечення, таких як одна відповідальність і DRY. Ось чому навіть люди, які їх люблять, вважають їх часом проблематичними.
Джефф Етвуд викладає іншу інтерпретацію в публікації в блозі, обговорюючи цю цитату: Регулярні вирази: Тепер у вас є дві проблеми (спасибі Ейфорику за посилання)
Аналізуючи повний текст публікацій Джеймі в оригінальній темі 1997 року, ми виявляємо наступне:
Природа Перла заохочує використання регулярних виразів майже до виключення всіх інших прийомів; вони далеко і є найбільш "очевидним" (принаймні, людям, які не знають кращого) способу дістатися від точки А до точки Б.
Перша цитата - занадто гліб, щоб сприймати її серйозно. Але з цим я повністю згоден. Ось то, що Джеймі намагався зробити: не те, що регулярні вирази самі по собі є злими, але в тому, що надмірне використання регулярних виразів є злом.
Навіть якщо ви дійсно в повній мірі зрозуміти регулярні вирази, ви біжите в The Golden Hammer проблеми, намагаючись вирішити проблему з регулярними виразами, коли це було б простіше і зрозуміліше , щоб зробити те ж саме з регулярним кодом (дивись також CodingHorror: Regex використання проти зловживання Regex ).
Є ще одна публікація в блозі, в якій розглядається контекст цитати, і йдеться про детальніше, ніж Етвуд: Блог Джефрі Фрідла: Джерело відомої цитати "Зараз у вас є дві проблеми".
З цією цитатою відбувається кілька речей.
Цитата є повторенням більш раннього анекдоту:
Щоразу, коли стикаються з проблемою, деякі люди кажуть "Дозвольмо використовувати AWK". Зараз у них дві проблеми. - Д. Тілбрук
Це жарт і справжнє копання, але це також спосіб виділення регулярного вираження як поганого рішення, пов'язуючи його з іншими поганими рішеннями. Це чудовий ха-ха лише серйозний момент.
Для мене - зауважте, ця цитата цілеспрямовано відкрита для тлумачення - сенс прямо спрямований. Просто оголошення ідеї використання регулярного виразу не вирішило проблему. Крім того, ви збільшили когнітивну складність коду, додавши додаткову мову з правилами, що відрізняються від мови, якою ви користуєтесь.
Хоча смішно, як жарт, вам потрібно порівняти складність нерегексичного рішення зі складністю рішення регулярного виразів + додаткова складність включення регулярних виразів. Можливо, варто вирішити проблему з регулярним виразом, незважаючи на додаткові витрати на додавання регулярних виразів.
Регулярні висловлювання відомі з урахуванням змісту; дійсно виражається вірогідним обґрунтуванням згаданого змісту; але, на жаль, на жаль, зрештоюється репутація причин, що виконуються, але, на жаль, нереалізується, анонсується, аральдоне не значить
(Регулярні вирази не є гіршими для читання чи підтримки, ніж будь-який інший неформатований вміст; дійсно, регулярний вираз тут, мабуть, легше читати, ніж цей фрагмент тексту, - але, на жаль, вони мають погану репутацію, оскільки деякі реалізації не дозволяють форматувати і людей взагалі не знаю, що ти можеш це зробити.)
Ось тривіальний приклад:
^(?:[^,]*+,){21}[^,]*+$
Що насправді не так складно читати чи підтримувати, але це ще простіше, коли це виглядає так:
(?x) # enables comments, so this whole block can be used in a regex.
^ # start of string
(?: # start non-capturing group
[^,]*+ # as many non-commas as possible, but none required
, # a comma
) # end non-capturing group
{21} # 21 of previous entity (i.e. the group)
[^,]*+ # as many non-commas as possible, but none required
$ # end of string
Це трохи надмірний приклад (коментування $
подібне до коментування i++
), але, очевидно, не повинно бути проблем з читанням, розумінням та підтримкою цього.
Поки вам зрозуміло, коли підходять регулярні вирази і коли вони погана ідея, в них нічого поганого, і в більшості випадків цитата JWZ насправді не застосовується.
*+
? Чим це відрізняється (функціонально) від просто *
?
*+
цьому випадку робити буквально немає сенсу ; все закріплено і може за один проїзд зіставитись автоматом, який може нараховувати до 22. Правильний модифікатор для цих наборів без комах є просто старим *
. (Більше того, тут також не повинно бути різниць між жадібними та не жадібними алгоритмами відповідності. Це надзвичайно простий випадок.)
На додаток до відповіді ChrisF - що регулярні вирази "важко кодувати , зрозуміти та підтримувати", є ще гірше: вони просто досить потужні, щоб обдурити людей, намагаючись розібрати їх, щоб вони не могли, наприклад, HTML. Дивіться численні запитання на тему "як я розбираю HTML?" Наприклад, єдина найбільш епічна відповідь у всьому ТАК!
Регулярні вирази дуже потужні, але у них є одна маленька і одна велика проблема; їх важко написати, і майже неможливо читати.
У кращому випадку використання регулярного виразу вирішує проблему, тож тоді у вас є лише проблема обслуговування складного коду. Якщо ви не знайдете регулярний вираз правильно, у вас є і вихідна проблема, і проблема з нечитабельним кодом, який не працює.
Іноді регулярні вирази називаються кодом лише для запису. Зіткнувшись із регулярним виразом, який потребує виправлення, часто швидше починати з нуля, ніж намагатися зрозуміти вираз.
Проблема полягає в тому, що регулярний гекс є складним звіром, і ви вирішуєте свою проблему лише в тому випадку, якщо регекс використовуєте ідеально. Якщо цього не сталося, у вас виникають дві проблеми: ваша початкова проблема і регулярний вираз.
Ви стверджуєте, що він може виконати роботу сотні рядків коду, але ви також можете зробити аргумент, що 100 рядків чіткого, стислого коду краще, ніж один рядок регулярного виразів.
Якщо вам потрібні певні докази цього: Ви можете перевірити цей SO Classic або просто розчесати тег SO Regex
Значення має дві частини:
Коли ви запитаєте про це у 2014 році, було б цікаво зосередитись на ідеологіях мов програмування контексту 1997 року порівняно з сучасним. Я не буду тут вступати в цю дискусію, але думки про Perl і Perl дуже змінилися.
Однак, щоб зупинитися на контексті 2013 року ( de l'eau a coulé sous les ponts depuis), я б запропонував зосередитись на реконструкції в цитатах, використовуючи відомий комікс XKCD, який є прямою цитатою твору Джеймі Завінського :
По- перше у мене були проблеми , щоб зрозуміти цей комікс , тому що це було посилання на Завінського цитатою, і цитата з Джей-Z тексти пісень, і посилання ГНУ program --help -z
прапор 2 , так, що це було занадто багато культури для мене , щоб зрозуміти це.
Я знав, що це весело, я відчував це, але я не знав чому. Люди часто жартують з приводу Perl та реджексів, тим більше, що це не найшвидша мова програмування, насправді не знаю, чому це повинно бути весело ... Можливо, тому, що зловмисники Perl роблять дурні речі .
Тож початкова цитата здається саркастичним жартом, заснованим на реальних життєвих проблемах (біль?), Спричинених програмуванням інструментами, які шкодять. Так само, як молоток може завдати шкоди муляру, програмуючи інструменти, які не є тими, які розробник обрав би, якщо він може нашкодити (мозок, почуття). Іноді виникають великі дебати щодо того, який інструмент найкращий, але це майже нічого не варто, оскільки це проблема вашого смаку чи смаку вашої команди програмування , культурних чи економічних причин. Ще один чудовий комікс XKCD про це:
Я можу зрозуміти, що люди відчувають біль від регулярних виразів, і вони вважають, що інший інструмент краще підходить для того, для чого призначені регулярні виразки. Коли @ karl-bielefeldt відповідає на ваше запитання з великою експресивністю , ця відповідальність приносить велику відповідальність , і реджекси особливо хвилюють це. Якщо розробник не піклується про те, як s-він поводиться з регулярними виразами, це врешті-решт буде болем для людей, які підтримуватимуть код пізніше.
Я закінчу цією відповіддю про реконструкцію котирувань цитатою, що показує типовий приклад з найкращих практик « Perl Best Practices» від Damian Conw ay (книга 2005 року).
Він пояснює, що писати такий зразок:
m{'[^\\']*(?:\\.[^\\']*)*'}
... не є більш прийнятним, ніж написання такої програми :
sub'x{local$_=pop;sub'_{$_>=$_[0
]?$_[1]:$"}_(1,'*')._(5,'-')._(4
,'*').$/._(6,'|').($_>9?'X':$_>8
?'/':$")._(8,'|').$/._(2,'*')._(
7,'-')._(3,'*').$/}print$/x($=).
x(10)x(++$x/10).x($x%10)while<>;
Але це можна переписати , це все ще не симпатично, але принаймні зараз воно приживеться.
# Match a single-quoted string efficiently...
m{ ' # an opening single quote
[^\\']* # any non-special chars (i.e., not backslash or single quote)
(?: # then all of...`
\\ . # any explicitly backslashed char
[^\\']* # followed by any non-special chars
)* # ...repeated zero or more times
' # a closing single quote
}x
Цей вид прямокутної форми є другою проблемою, а не регулярними виразами, які можна форматувати чітким, доступним та читабельним способом.
/* Multiply the first 10 values in an array by 2. */ for (int i = 0 /* the loop counter */; i < 10 /* continue while it is less than 10 */; ++i /* and increment it by 1 in each iteration */) { array[i] *= 2; /* double the i-th element in the array */ }
Якщо є одне, чого слід дізнатися з інформатики, це ієрархія Хомського . Я б сказав, що всі проблеми з регулярними виразами виникають із спроб розбору з ним контекстної граматики. Коли ви можете встановити обмеження (або вважаєте, що можете встановити обмеження) рівнів вкладення в CFG, ви отримуєте ці довгі і складні регулярні вирази.
Регулярні вирази більше підходять для токенізації, ніж для повномасштабного синтаксичного аналізу.
Але, напрочуд великий набір речей, які програмістам потрібно розбирати, можна проаналізувати звичайною мовою (або, що ще гірше, майже піддається розбору звичайною мовою, і якщо ви напишете лише трохи більше коду ...).
Отже, якщо хтось звик до "ага, мені потрібно підібрати текст окремо, я буду використовувати регулярний вираз", легко піти вниз по цьому маршруту, коли вам потрібно щось, що ближче до висувного автомата, аналізатора CFG або ще потужніші граматики. Зазвичай це закінчується сльозами.
Отже, я думаю, що цитата не стільки грюкає регулярними виразками, вони користуються ними (і добре використовуються, вони дуже корисні), але надмірна залежність від регулярних виразів (або, конкретно, некритичного їх вибору) .
jwz просто зі свого рокера з цією цитатою. регулярні вирази не відрізняються від будь-яких мовних особливостей - простий у викручуванні, важкий у використанні елегантно, потужний часом, невідповідний часом, часто добре документований, часто корисний.
те саме можна сказати для арифметики з плаваючою комою, замикань, орієнтації на об'єкти, асинхронного вводу / виводу або будь-якого іншого, що ви можете назвати. якщо ви не знаєте, чим займаєтесь, мови програмування можуть зробити вас сумними.
якщо ви думаєте, що регулярні вирази важко прочитати, спробуйте прочитати еквівалентну програму аналізатора, щоб споживати відповідний шаблон. Часто реджекси виграють, оскільки вони більш компактні, ніж повноцінні аналізатори ... і в більшості мов вони також швидші.
не відмовляйтеся від використання регулярних виразів (чи будь-якої іншої мови), оскільки блогер, що саморекламує, робить некваліфіковані заяви. спробуйте все для себе і подивіться, що для вас працює.
Мою улюблену глибоку відповідь на це дає відомий Роб Пайк у публікації в блозі, відтвореній із внутрішнього коментаря до коду Google: http://commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- і.html
Підсумок полягає в тому, що вони не погані , але їх часто використовують для завдань, для яких вони не обов'язково підходять, особливо якщо мова йде про лексику та аналіз певних даних.
Регулярні вирази важко записати, важко записати, і вони можуть бути дорогими відносно інших технологій ... Лексери, з іншого боку, досить легко писати правильно (якщо не настільки компактно), і дуже легко перевірити. Розглянемо пошук буквено-цифрових ідентифікаторів. Написати не регулярно (наприклад, "[a-ZA-Z _] [a-ZA-Z_0-9] *"), але насправді не набагато складніше написати як простий цикл. Однак продуктивність циклу буде значно вищою і буде містити набагато менше коду під обкладинками. Бібліотека регулярних виразів - це велика річ. Використовувати один для розбору ідентифікаторів - це як використовувати Ferrari, щоб піти в магазин за молоком.
Він говорить набагато більше того, стверджуючи, що регулярні вирази корисні, наприклад, одноразове відповідність шаблонів у текстових редакторах, але їх рідко слід використовувати у складеному коді тощо. Варто прочитати.
Це пов’язано з епіграмою № 34 Алана Перліса:
Рядок - це настільна структура даних, і скрізь, де вона передається, відбувається багато дублювання процесу. Це ідеальний засіб для приховування інформації.
Отже, якщо ви обрали рядок символів як структуру даних (і, природно, код на основі регулярних виразів як алгоритми для управління ним), у вас виникає проблема, навіть якщо вона працює: поганий дизайн навколо невідповідного представлення даних, який важко розширення та неефективність.
Однак часто це не працює: оригінальна проблема не вирішена, і тому в цьому випадку у вас є дві проблеми.
Реджекси широко використовуються для швидкого та брудного розбору тексту. Вони є чудовим інструментом для вираження візерунків, які є трохи складнішими, ніж просто збіг рядків.
Однак, коли реджекси отримують більш складні сервальні питання, вони піднімають голову.
Таким чином, все занадто просто починати з проблеми обробки тексту, застосовувати до неї регулярні вирази і закінчувати двома проблемами, оригінальною проблемою, яку ви намагалися вирішити, і мати справу з регулярними виразами, які намагаються вирішити (але не вирішувати правильно) первісна проблема.