Коли НЕ слід використовувати регулярні вирази? [зачинено]


50

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

Простий приклад №1 - це розбір HTML за допомогою regexp - відомої дороги до численних помилок. Напевно, це також пов’язано з розбором загалом.

Але, чи існують інші чітко невисокі області для регулярних виразів?


ps: " Питання, яке ви задаєте, видається суб'єктивним і, ймовірно, буде закритим. " - таким чином, я хочу підкреслити, що мене цікавлять приклади, коли використання регулярних виразів, як відомо, викликає проблеми.


9
Розбір HTML за допомогою regexp - це не просто "відома дорога до численних помилок". Це насправді неможливо .
Крамій Відновити Моніку

19
Це не тільки неможливо, це також призводить до божевілля та вічного прокляття
Мартін Вікман

3
@ Йорг: Regexp - це лише абревіатура для регулярного вираження.
Джорен

3
@ Йорг: Дуже вірно, що між регулярними виразами з математики та їх реалізацією в бібліотеках програмного забезпечення існує велика різниця. Правда також, що більшість регулярних бібліотек виразів мають розширення, які дозволяють їм виходити за рамки прийняття лише регулярних мов, і називати їх регулярними виразами не завжди так доречно. Я згоден з вами, що є дві різні концепції. Але вони мають те саме ім’я; regexp - це все ще лише абревіатура, а не сам термін. На цьому сайті є багато прикладів використання повного терміна для бібліотек програмного забезпечення.
Joren

2
@ Йорг - це семантика. Хоча може бути гарною ідеєю називати ці шаблони різними іменами (якщо тільки уникнути помилок "регулярні вирази для звичайних мов"), "regexp" / "регулярні вирази" - не дуже вдала спроба, і лише призводить до додаткова плутанина.
Кобі

Відповіді:


60

Не використовуйте регулярні вирази:

  • Коли є парсери.

Це не обмежується HTML . Простий дійсний XML не може бути розумно розібраний з регулярним виразом, навіть якщо ви знаєте схему і знаєте, що вона ніколи не зміниться.

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

  • Більш загально, коли у вас є кращі інструменти для виконання своєї роботи.

Що робити, якщо потрібно шукати лист, малий і великий? Якщо ви любите регулярні вирази, ви їх будете використовувати. Але хіба не легше / швидше / читабельніше скористатися двома пошуками один за одним? Швидше за все, на більшості мов ви досягнете кращої продуктивності та зробите свій код читабельнішим.

Наприклад, зразок коду у відповіді Інго є хорошим прикладом, коли ви не повинні використовувати регулярні вирази. Просто шукайте foo, а потім bar.

  • При розборі людської писемності.

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

  • Під час перевірки деяких типів даних.

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

Без правильних інструментів ви будете робити помилки. І ви їх помітите в останній момент, а може, і ніколи. Якщо вам не байдуже чистий код, ви напишете рядок із двадцяти рядків без коментарів, пробілів і нових рядків.

  • Коли ваш код буде прочитаний. А потім читайте знову, і знову і знову, кожен раз різними розробниками.

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


9
"Якщо серйозно, якщо я беру ваш код і повинен його переглянути чи змінити, я не хочу витрачати тиждень, намагаючись зрозуміти двадцять рядків з великою кількістю символів". +1!
funkybro

1
Це набагато краща відповідь, ніж її крокова сестра щодо переповнення стека: stackoverflow.com/questions/7553722/…
Кобі

1
Якщо ви використовуєте Perl / PCRE (і, мабуть, інші сучасні аромати регулярних виразів), прочитайте про підпрограми, названі групи захоплення та (?(DEFINE))твердження;) Ви можете писати дуже чисті регексери, використовуючи ті, і фактично, коли ви використовуєте ці, ви будете писати граматики, які є дуже схоже на те, що ви писали б у yacc або подібних;)
NikiC

2
Використання регулярних виразів для розбору слів у чорному списку - чітка помилка.
Dan Ray

У світі немає жодних причин уникати накидання регулярного виража на рядок типу "<a href='foo'>stuff</a>". Сучасні реджекси з цим не мають проблем.
tchrist

18

Найголовніше: коли мова, яку ви розбираєте, не є звичайною мовою .

HTML це НЕ звичайну мову і розбір його формальному виразу є НЕ можливо (не тільки важко або дорога до баггі коду).


4
Неправильно! Якщо ви використовуєте будь-який із сучасних ароматів регулярних виразів (Perl, PCRE, Java, .NET, ...), ви можете робити рекурсії та твердження, і таким чином можна розібрати також відповідність контексту і граматикам, що не залежать від контексту.
NikiC

9
@NikiC. Не помиляється. "Сучасні аромати регулярного вираження" не є регулярними виразами (які можна використовувати для розбору звичайних мов, звідси і назва). Я погоджуюся, що з PRE ви можете зробити більше, але я б не називав їх просто "регулярними виразами" (як в оригінальному питанні).
Маттео

1
Сучасні регулярні виразки набагато виходять за рамки того, що ваша бабуся навчила, що регулярні виразки могли це зробити, тому що її поради несуттєві. І навіть примітивні регулярні виразки можуть обробляти більшість маленьких фрагментів HTML. Ця ковдра заборона смішна і нереальна. Для подібних речей були зроблені реджекси . І так, я знаю, про що я говорю .
tchrist

12

На stackoverflow часто можна побачити, як люди запитують регулярні вирази, які з'ясовують, чи не містить певний рядок те чи інше . Це, ІМХО, перевертаючи мету регулярного вираження. Навіть якщо існує рішення (використовуючи негативний погляд за твердженнями або подібними матеріалами), часто набагато краще використовувати регулярний вираз для того, для чого він був створений, і обробляти негативний випадок програмною логікою.

Приклад:

# bad
if (/complicated regex that assures the string does NOT conatin foo|bar/) {
    # do something
}

# appropriate
if (/foo|bar/) {
    # error handling
} else {
    # do something
}

1
+1: Кілька разів я уникав кодування себе в куточок з регулярними виразами, зупиняючись і запитуючи себе: "Гаразд, що я конкретно намагаюся відповідати?" а не "Чого я намагаюся уникати?"

5

Два випадки:

Коли є простіший шлях

  • Більшість мов надають просту функцію, як INSTR, щоб визначити, чи є одна рядок підмножиною іншої. Якщо це те, що ви хочете зробити, використовуйте більш просту функцію. Не пишіть власний регулярний вираз.

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

Коли регулярні вирази недостатньо потужні

  • Якщо вам потрібен аналізатор, використовуйте аналізатор.

0

Регулярні вирази не можуть ідентифікувати рекурсивні структури . Це основне обмеження.

Візьміть JSON - це досить простий формат, але оскільки об’єкт може містити інші об'єкти як значення членів (довільно глибокі), синтаксис є рекурсивним і не може бути розбитий регулярним виразом. З іншого боку, CSV може бути проаналізований регулярними виразами, оскільки він не містить рекурсивних структур.

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

Зауважте, це не має нічого спільного з тим, наскільки складним чи перекрученим є формат. S-вирази дійсно дуже прості, але їх неможливо розібрати за допомогою регулярного вираження. CSS2, з іншого боку, є досить складною мовою, але не містить рекурсивних структур і для цього може бути розібраний з регулярним виразом. (Хоча це не вірно для CSS3 через вирази CSS, які мають рекурсивний синтаксис.)

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

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

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

Отож, коли ви чуєте, що HTML або C # не можна розбирати за допомогою регулярних виразів, пам’ятайте, що регулярні вирази все ще є важливою частиною аналізаторів. Ви просто не можете розбирати таку мову, використовуючи лише регулярні вирази та без допоміжного коду.

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