JavaRush /Java блог /Random UA /Регулярні вирази в Java, частина 1
Professor Hans Noodles
41 рівень

Регулярні вирази в Java, частина 1

Стаття з групи Random UA
Пропонуємо до вашої уваги переклад короткого посібника з регулярних виразів у мові Java, написаного Джеффом Фрісеном (Jeff Friesen) для сайту JavaWorld . Для простоти читання ми розділабо статтю кілька частин. Регулярні вирази у Java, частина 1 - 1

Використання API регулярних виразів у програмах на мові Java для розпізнавання та опису шаблонів

Символьні та різноманітні рядкові типи даних мови Java забезпечують низькорівневу підтримку пошуку за шаблоном, але їх використання з цією метою зазвичай сильно ускладнює код. Простіший і продуктивніший код виходить при використанні API Regex ("API регулярних виразів"). Ця інструкція допоможе вам розпочати роботу з регулярними виразами та API Regex. Спочатку ми обговоримо загалом три найцікавіші класи з пакета java.util.regex, а потім заглянемо в клас Patternі вивчимо його витончені конструкції пошуку за шаблонами. Увага: завантажити вихідний код (створений Джеффом Фрізеном для сайту JavaWorld) демо-додатки з цієї статті можна звідси .

Що таке регулярні вирази?

Регулярний вираз (regular expression/regex/regexp) — це рядок, який є шаблоном (pattern), який описує набір рядків. Шаблон визначає, які саме рядки належать до набору. Шаблон складається з літералів та метасимволів - символів зі спеціальним, а не буквальним значенням. Пошук за шаблоном - це пошук за текстом для знаходження збігів, тобто рядків, що відповідають шаблону регулярного виразу. Java підтримує пошук за шаблоном за допомогою API Regex. Цей API складається з трьох класів: Pattern, Matcherі PatternSyntaxException, розміщених у пакеті java.util.regex:
  • об'єкти класу Pattern, звані також шаблонами, є скомпіловані регулярні висловлювання.
  • об'єкти класу Matcher, чи співставники, є механізми інтерпретації шаблонів виявлення збігів у символьних послідовностях (об'єктах, чиї класи реалізують інтерфейс java.lang.CharSequenceі є джерелами тексту).
  • Об'єкти класу PatternSyntaxExceptionслужать для опису неприпустимих шаблонів регулярних виразів.
Java також надає підтримку пошуку за шаблоном за допомогою різних методів класу java.lang.String. Наприклад, функція boolean matches (String regex)повертає trueтільки тоді, коли рядок, що викликає, в точності відповідає регулярному виразу regex.
Зручні методи
matches()та інші орієнтовані на регулярні вирази зручні методи класу String"під капотом" реалізовані аналогічно API Regex.

RegexDemo

Я створив додаток RegexDemoдля демонстрації регулярних виразів мови Java та різних методів класів Pattern, Matcherта PatternSyntaxException. Нижче вихідний код цього демо-додатка. Лістинг 1. Демонстрація регулярних виразів
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class RegexDemo
{
   public static void main(String[] args)
   {
      if (args.length != 2)
      {
         System.err.println("usage: java RegexDemo regex input");
         return;
      }
      // Преобразуем символьные последовательности начала новой строки (\n) в символы начала строки.
      args[1] = args[1].replaceAll("\\\\n", "\n");
      try
      {
         System.out.println("regex = " + args[0]);
         System.out.println("input = " + args[1]);
         Pattern p = Pattern.compile(args[0]);
         Matcher m = p.matcher(args[1]);
         while (m.find())
            System.out.println("Found [" + m.group() + "] starting at "
                               + m.start() + " and ending at " + (m.end() - 1));
      }
      catch (PatternSyntaxException pse)
      {
         System.err.println("Неправильное регулярное выражение: " + pse.getMessage());
         System.err.println("Описание: " + pse.getDescription());
         System.err.println("Позиция: " + pse.getIndex());
         System.err.println("Неправильный шаблон: " + pse.getPattern());
      }
   }
}
Перше, що робить метод mainкласу RegexDemo– перевіряє свій командний рядок. Він вимагає двох аргументів: перший — регулярне вираження, а другий — вхідний текст, у якому виконуватиметься пошук за цим регулярним виразом. Можливо, вам знадобиться використовувати вхідний текст символ нового рядка (\n). Зробити це можна лише вказавши символ \, за яким слідує символ n. Функція main()перетворює цю символьну послідовність Unicode-значення 10. Регулярні вирази у Java, частина 1 - 2Основна частина коду RegexDemoукладена в конструкцію try-catch. Блок tryспочатку виводить заданий регулярний вираз та вхідний текст, після чого створює об'єктPattern, що зберігає скомпільований регулярний вираз (регулярні вирази компілюються задля покращення продуктивності пошуку за шаблоном). Зіставник витягується з об'єкта Patternі використовується для багаторазового пошуку збігів, доки не будуть знайдені всі. Блок catchвикликає кілька методів класу PatternSyntaxExceptionдля отримання корисної інформації про виключення. Ця інформація послідовно виводиться у потік виведення. Необхідності знати деталі роботи коду поки що немає: вони проясняться, коли ми вивчатимемо API у другій частині статті. Однак необхідно скомпілювати листинг 1. Візьміть код із лістингу 1, після чого наберіть у командному рядку наступну команду для компіляції RegexDemo: javac RegexDemo.java

Клас Pattern та його конструкції

Клас Pattern, перший із трьох класів, складових API Regex, є скомпільоване уявлення регулярного висловлювання. Документація SDK класу Patternописує різноманітні конструкції регулярних виразів, але якщо ви не користуєтеся активно регулярними виразами, то окремі частини цієї документації можуть поставити вас у глухий кут. Що таке квантифікатори (quantifiers) і в чому різниця між жадібними (greedy), лінивими (reluctant) та над жадібними (possessive) квантифікаторами? Що таке класи символів (character classes), граничні зіставники (boundary matchers), зворотні посилання (back references) та вкладені прапорові вирази (embedded flag expressions)? Я відповім на ці та інші питання у наступних розділах.

Літеральні рядки

Найпростіша конструкція регулярного виразу – літеральний рядок. Для успіху пошуку за шаблоном будь-яка частина вхідного тексту має відповідати шаблону цієї конструкції. Розглянемо наступний приклад: java RegexDemo apple applet У цьому прикладі ми намагаємося знайти відповідність шаблону appleу вхідному тексті applet. У наступному результаті показано знайдений збіг:
regex = apple
input = applet
Found [apple] starting at 0 and ending at 4
Ми бачимо у виведених даних регулярний вираз і вхідний текст, а потім вказівку на успішне виявлення appleв applet. Крім того, там наведені початкова та кінцева позиції цього збігу: 0і 4відповідно. Початкова позиція вказує на перше місце в тексті, де виявлено збіг з шаблоном, а кінцева позиція — останню точку збігу. Тепер припустимо, що ми задали наступний командний рядок: java RegexDemo apple crabapple На цей раз ми отримаємо наступний результат, з іншими початковими та кінцевими позиціями:
regex = apple
input = crabapple
Found [apple] starting at 4 and ending at 8
У протилежному випадку, як appletрегулярного висловлювання і apple— вхідного тексту, збігів знайдено не буде. Збіг має весь регулярний вираз, а в даному випадку, вхідний текст не містить tпісля apple. Регулярні вирази у Java, частина 1 - 3

Метасимволи

Цікавіші конструкції регулярних виразів поєднують літеральні символи з метасимволами. Наприклад, у регулярному вираженні a.b, метасимвол точки (.)означає будь-який символ, що знаходиться між ab і b. Розглянемо наступний приклад: java RegexDemo .ox "The quick brown fox jumps over the lazy ox." У цьому прикладі використовується .oxяк регулярний вираз, і The quick brown fox jumps over the lazy ox.як вхідний текст. RegexDemoвиконує пошук у тексті збігів, що починаються з будь-якого символу та закінчуються на ox.Результати його виконання наступні:
regex = .ox
input = The quick brown fox jumps over the lazy ox.
Found [fox] starting at 16 and ending at 18
Found [ ox] starting at 39 and ending at 41
У виведених результатах бачимо два збіги: foxі ox(із символом пробілу перед ним). Метасимвол . відповідає символу fу першому випадку та пробілу – у другому. Що ж станеться, якщо замінити .oxметасимволом .? Тобто ми отримаємо в результаті наступного командного рядка: java RegexDemo . "The quick brown fox jumps over the lazy ox." Оскільки метасимвол точки відповідає будь-якому символу, RegexDemoвиведе знайдені збіги для всіх символів (включаючи завершальний символ точки) вхідного тексту:
regex = .
input = The quick brown fox jumps over the lazy ox.
Found [T] starting at 0 and ending at 0
Found [h] starting at 1 and ending at 1
Found [e] starting at 2 and ending at 2
Found [ ] starting at 3 and ending at 3
Found [q] starting at 4 and ending at 4
Found [u] starting at 5 and ending at 5
Found [i] starting at 6 and ending at 6
Found [c] starting at 7 and ending at 7
Found [k] starting at 8 and ending at 8
Found [ ] starting at 9 and ending at 9
Found [b] starting at 10 and ending at 10
Found [r] starting at 11 and ending at 11
Found [o] starting at 12 and ending at 12
Found [w] starting at 13 and ending at 13
Found [n] starting at 14 and ending at 14
Found [ ] starting at 15 and ending at 15
Found [f] starting at 16 and ending at 16
Found [o] starting at 17 and ending at 17
Found [x] starting at 18 and ending at 18
Found [ ] starting at 19 and ending at 19
Found [j] starting at 20 and ending at 20
Found [u] starting at 21 and ending at 21
Found [m] starting at 22 and ending at 22
Found [p] starting at 23 and ending at 23
Found [s] starting at 24 and ending at 24
Found [ ] starting at 25 and ending at 25
Found [o] starting at 26 and ending at 26
Found [v] starting at 27 and ending at 27
Found [e] starting at 28 and ending at 28
Found [r] starting at 29 and ending at 29
Found [ ] starting at 30 and ending at 30
Found [t] starting at 31 and ending at 31
Found [h] starting at 32 and ending at 32
Found [e] starting at 33 and ending at 33
Found [ ] starting at 34 and ending at 34
Found [l] starting at 35 and ending at 35
Found [a] starting at 36 and ending at 36
Found [z] starting at 37 and ending at 37
Found [y] starting at 38 and ending at 38
Found [ ] starting at 39 and ending at 39
Found [o] starting at 40 and ending at 40
Found [x] starting at 41 and ending at 41
Found [.] starting at 42 and ending at 42
Метасимволи лапок
Щоб вказати .будь-який інший метасимвол як літеральний символ у конструкції регулярного виразу, необхідно екранувати його одним з наступних способів:
  • поставити перед ним символ зворотної косої межі;
  • Помістити цей метасимвол між \Qта \E(наприклад, \Q.\E).
Не забувайте дублювати всі, що зустрічаються в рядковому літералі, наприклад, String regex = "\\.";символи зворотної косої риси (наприклад, \\.або \\Q.\\E). Не дублюйте ті символи зворотної косої межі, які є частиною аргументу командного рядка.

Класи символів

Іноді доводиться обмежувати шукані збіги конкретним набором символів. Наприклад, шукати в тексті голосні a, e, i, oі u, причому кожне входження голосної літери вважається збігом. У вирішенні подібних завдань нам допоможуть класи символів, які задають набори символів між метасимволами квадратних дужок ( [ ]). Клас Patternпідтримує прості класи символів, діапазонні класи, інверсію, об'єднання, перетин та віднімання класів. Ми розглянемо всі їх зараз.

Прості класи символів

Простий клас символів складається з розміщених пліч-о-пліч символів і задає відповідність тільки цим символам. Наприклад, клас [abc]відповідає символам a, bта c. Розглянемо наступний приклад: java RegexDemo [csw] cave Як видно з результатів, у цьому прикладі буде знайдено лише символ c, для якого є відповідність до cave:
regex = [csw]
input = cave
Found [c] starting at 0 and ending at 0

Інвертовані класи символів

Інвертований клас символів починається з метасимволу ^і задає відповідність лише тим символам, які не містяться. Наприклад, клас [^abc]відповідає всім символам, крім a, bта c. Розглянемо наступний приклад: java RegexDemo "[^csw]" cave Зверніть увагу, що на моїй операційній системі (Windows) необхідні подвійні лапки, оскільки командна оболонка розглядає ^як символ, що екранує. Як бачимо, в цьому прикладі знайдені тільки символи a, vі eдля яких є відповідності в cave:
regex = [^csw]
input = cave
Found [a] starting at 1 and ending at 1
Found [v] starting at 2 and ending at 2
Found [e] starting at 3 and ending at 3

Діапазонні класи символів

Діапазонний клас символів складається із двох символів, розділених символом дефісу ( -). Усі символи, починаючи з символу ліворуч від дефісу та закінчуючи символом праворуч, відносяться до діапазону. Наприклад, діапазон [a-z]відповідає всім латинським буквам у нижньому регістрі. Це еквівалентно завданням простого класу [abcdefghijklmnopqrstuvwxyz]. Розглянемо наступний приклад: java RegexDemo [a-c] clown У цьому прикладі буде знайдено лише символ c, для якого є відповідність до clown:
regex = [a-c]
input = clown
Found [c] starting at 0 and ending at 0
Регулярні вирази в Java, частина 2 Регулярні вирази в Java, частина 3 Регулярні вирази в Java, частина 4 Регулярні вирази в Java, частина 5
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ