JavaRush /Java блог /Random UA /RegEx: 20 коротких кроків для освоєння регулярних виразів...
Artur
40 рівень
Tallinn

RegEx: 20 коротких кроків для освоєння регулярних виразів. Частина 2

Стаття з групи Random UA
RegEx: 20 коротких кроків для освоєння регулярних виразів. Частина 1 Оригінал тут Минулої частини ми освоїли найпростіші регулярні висловлювання, і вже дечому навчабося. У цій частині ми вивчимо трохи більш складні конструкції, але, повірте, це буде не так важко, як могло б здатися. RegEx: 20 коротких кроків для освоєння регулярних виразів.  Частина 2 - 1Отже, продовжимо!

Крок 8: зірочка *та знак "плюс"+

RegEx: 20 коротких кроків для освоєння регулярних виразів.  Частина 2 - 2Досі нам більш-менш вдавалося зіставляти лише рядки заданої довжини. Але в останніх завданнях ми наблизабося до межі того, що ми можемо зробити за допомогою позначень, які ми бачабо й досі. Припустимо, наприклад, що ми не обмежені 3-символьними ідентифікаторами Java, а ми можемо мати ідентифікатори будь-якої довжини. Рішення, яке могло працювати в попередньому прикладі, не працюватиме в наступному прикладі:
pattern: [a-zA-Z_$]\w\w 
string:   __e $12 3 3.2 fo Bar r a23 mm ab x
matches: ^^^ ^^^ ^^^ ^^^  
( Приклад ) Зверніть увагу, що коли ідентифікатор дійсний, але довше 3 символів, порівнюються лише перші три символи. І коли ідентифікатор є дійсним, але містить менше 3 символів, то regex його взагалі не знаходить! Проблема в тому, що вирази в квадратних дужках []відповідають одному символу, як і класи символів, такі як \w. Це означає, що будь-які збіги в наведеному вище регулярному виразі повинні бути рівно в три символи. Так що це не працює, як ми могли б сподіватися. Тут можуть допомогти спеціальні символи *та +. Це модифікатори, які можуть бути додані праворуч від будь-якого виразу, щоб шукати відповідності до цього виразу більше одного разу. Зірка Кліні (або "зірочка")*вкаже, що потрібно шукати відповідності попередньому токену будь-яку кількість разів, включаючи нуль разів. Знак "плюс" +вкаже, що потрібно шукати один чи кілька разів. Таким чином, вираз, який передує +, є обов'язковим (принаймні один раз), у той час як вираз, який передує *, є необов'язковим, але коли він з'являється, він може з'являтися будь-яку кількість разів. Тепер, з цим знанням ми можемо виправити наведений вище регулярний вираз:
pattern: [a-zA-Z_$]\w* 
string:   __e $123 3.2 fo Barr a23mm ab x 
матчі: ^^^ ^^^^ ^^ ^^^^ ^^^^^ ^^ ^ 
( Приклад ) Тепер ми зіставляємо дійсні ідентифікатори будь-якої довжини! Бінґо! Але що б сталося, якби ми в прикладі вище використовували +замість *?
pattern: [a-zA-Z_$]\w+ 
string:   __e $123 3.2 fo Barr a23mm ab x
matches: ^^^ ^^^^ ^^ ^^^^ ^^^^^ ^^ 
( Приклад ) Ми пропустабо останній збіг х,. Це з тим, що це потрібно +зіставлення хоча б одного символу, але оскільки вираз у дужках [], попереднє \w+, вже 'з'їло' символ x, то доступних символів більше залишилося, тому зіставлення зірвалася. Коли ми можемо використовувати +? Коли нам потрібно знайти хоча б один збіг, але не важливо, скільки разів повинен збігатися цей вислів. Наприклад, якщо ми хочемо знайти будь-які числа, що містять десяткову точку:
pattern: \d*\.\d+ 
string:   0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 
matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^  
( Приклад ) Зверніть увагу, Що зробивши числа зліва від десяткової точки необов'язковими, ми змогли знайти як 0.011 так і .2. Для цього нам потрібно було зіставити рівно одну десяткову точку за допомогою \.і щонайменше одну цифру праворуч від десяткової точки за допомогою \d+. Вищезгаданий регулярний вираз не буде відповідати числу, подібному 3., тому що для відповідності нам потрібно принаймні одна цифра праворуч від десяткової точки.

За традицією, вирішимо кілька простих завдань:

Знайдіть усі англійські слова у уривку нижче.
pattern:
string: 3 plus 3 is six but 4 plus three is 7
matches:    ^^^^ ^^ ^^^ ^^^ ^^^^ ^^^^^ ^^ 
( Рішення ) Знайдіть усі позначення розмірів файлів у списку нижче. Розміри файлів будуть складатися з числа (з десятковою точкою або без неї), за яким слідують KB, MB, GBабо TB:
pattern:
string:   11TB 13 14.4MB 22HB 9.9GB TB 0KB 
matches: ^^^^ ^^^^^^ ^^^^^ ^^^  
( Рішення )

Крок 9: "опційний" знак питання?

RegEx: 20 коротких кроків для освоєння регулярних виразів.  Частина 2 - 3Ви вже написали regex для вирішення останнього завдання? Це спрацювало? Тепер спробуйте застосувати його тут:
pattern:
string: 1..3KB 5...GB ..6TB
matches:  
Очевидно, що жодне з цих позначень не є допустимим розміром файлу, тому хороший регулярний вираз не повинен знаходити жодного з них. Рішення, яке я написав для вирішення останнього завдання, відповідає їм усім, принаймні частково:
pattern: \d+\.*\d*[KMGT]B 
string:   1..3KB  5...GB .. 6TB 
matches: ^^^^^^ ^^^^^^^ ^^^ 
( Приклад ) То в чому проблема? Насправді нам потрібно знайти тільки одну десяткову точку, якщо вона є. Але *допускає будь-яку кількість збігів, включаючи нуль. Чи є спосіб зіставляти лише нуль разів чи один раз? Але чи не більше одного разу? Звичайно є. "optional" ?це модифікатор, який відповідає нулю або одному з попередніх символів, але не більше:
pattern: \d+\.?\d*[KMGT]B 
string: 1.. 3KB 5...GB .. 6TB 
matches:     ^^^ ^^^ 
( Приклад ) Ми тут підійшли до рішення, але це ще не зовсім те, що нам треба. Трохи згодом ми побачимо, як це виправити, за кілька кроків.

А поки що вирішимо таке завдання:

У деяких мовах програмування (наприклад, Java) за деякими цілими числами та числами з плаваючою комою (точкою) можуть слідувати l/ Lта f/ F, щоб вказати, що вони повинні розглядатися як long / float (відповідно), а не як звичайні int / double. Знайдіть усі дійсні "long" числа в рядку нижче:
pattern:
string:   13L long 2l 19 L lL 0 
матчів: ^^^ ^^ ^^ ^ 
( Рішення )

Крок 10: знак "or" (або)|

RegEx: 20 коротких кроків для освоєння регулярних виразів.  Частина 2 - 4На кроці 8 у нас виникли деякі труднощі зі знаходженням різних типів чисел з плаваючою точкою:
pattern: \d*\.\d+ 
string:   0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 
matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^  
Наведений вище шаблон зіставляє числа з десятковою точкою і щонайменше одну цифру праворуч від десяткової точки. Але що, якщо ми хочемо порівняти такі рядки, як 0.? (Без цифр праворуч від десяткової точки.) Ми могли б написати такий регулярний вираз:
pattern: \d*\.\d* 
string:   0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. . 
matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^ ^^ ^ 
( Приклад ) Це відповідає 0., але це також відповідає одиночній точці ., як ви можете бачити вище. Насправді те, що ми намагаємося зіставити, це два різні класи рядків:
  1. числа з мінімум однією цифрою праворуч від десяткової точки
  2. числа з хоча б однією цифрою зліва від десяткової точки
Напишемо 2 наступні, незалежні один від одного регулярні вирази:
pattern: \d*\.\d+ 
string:   0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. .
matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^  
pattern: \d+\.\d* 
string:   0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. .
matches: ^^^^^ ^^^ ^^^^ ^^^^^ ^^^^^^^^ ^^ 
Ми бачимо, що в жодному з цих випадків підрядки 42, 5або 6не .знаходяться двигуном. Для отримання необхідного результату нам не завадило б об'єднати ці регулярки. Як ми можемо досягти цього? Знак "or" |дозволяє нам вказати у регулярному вираженні відразу кілька можливих послідовностей збігів. Подібно до того, як []дозволяє нам вказувати альтернативні одиночні символи, за допомогою знака "or" |ми можемо вказувати альтернативні багатосимвольні вирази. Наприклад, якщо ми хочемо знайти "собаку" або "кішку", ми могли б написати якось так:
pattern: \w\w\w 
string:   Obviously , a dog is a better pet t a n a cat .
matches: ^^^^^^^^^^ ^^^ ^^^^^^ ^^^ ^^^ ^^^ 
( Приклад ) ... але це відповідає всім потрійним послідовностям символів класу "word". Але "dog" та "cat" навіть не мають спільних букв, тому і квадратні дужки не допоможуть нам тут. Ось найпростіший регулярний вираз, який ми могли б використати, він відповідає обом і лише цим двом словам:
pattern: dog|cat 
string: Obviously, dog is better pet than a cat .
matches:               ^^^ ^^^ 
( Приклад ) Механізм регулярних виразів спочатку намагається зіставити всю послідовність ліворуч від знака |, але якщо йому це не вдається, то потім він намагається зіставити послідовність праворуч від знака |. Декілька знаків |також можуть бути об'єднані в ланцюжки для відповідності більш ніж двом альтернативним послідовностям:
pattern: dog|cat|pet 
string: Авжеж, a dog is better pet than a cat .
matches:               ^^^ ^^^ ^^^ 
( Приклад )

Тепер вирішимо чергову пару завдань, щоб краще засвоїти цей крок:

Використовуйте знак |, щоб виправити десятковий регулярний вираз, вказаний вище, і отримати такий результат:
pattern:
string:   0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. .
matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^ ^^ 
( Рішення ) Використовуйте знак |, класи символів, " optional " ?тощо., щоб створити одне регулярне вираз, відповідне як цілим числам, і числам з плаваючою комою (точкою), як обговорювалося у завданню наприкінці попереднього кроку (це завдання трохи складніше, так ;))
pattern:
string:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0. 
matches: ^^^ ^^ ^^^^ ^^ ^^^ ^^ ^^^ ^^  
( Рішення ) 20 коротких кроків для освоєння регулярних виразів. Частина 3 RegEx: 20 коротких кроків для освоєння регулярних виразів. Частина 4
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ