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

  1. Спрощуємо рішення поширених завдань програмування за допомогою API Regex
  2. ЗАВАНТАЖЕННЯ ВИХІДНОГО КОДА
  3. Вивчаємо API Regex
  4. Методи класу Pattern
  5. розбиття тексту
  6. Методи класу Matcher
  7. Додавання тексту в кінець
  8. заміна тексту

Пропонуємо вашій увазі переклад короткого керівництва за регулярними виразами в мові Java, написаного Джеффом Фрісеном (Jeff Friesen) для сайту javaworld . Для простоти читання ми розділили статтю на кілька частин. Пропонуємо вашій увазі переклад короткого керівництва за регулярними виразами в мові Java, написаного Джеффом Фрісеном (Jeff Friesen) для сайту   javaworld Регулярні вирази в Java, частина 1 Регулярні вирази в Java, частина 2

Спрощуємо рішення поширених завдань програмування за допомогою API Regex

У першій і другій частинах цієї статті ви познайомилися з регулярними виразами і API Regex. Ви дізналися про існування класу Pattern, пройшлися по прикладів, що демонструють конструкції регулярних виразів, від найпростішого пошуку за шаблоном на основі літеральних рядків до більш складного пошуку за допомогою діапазонів, граничних сопоставітелей і квантіфікаторов. У цій та наступних частинах ми розглянемо не охоплені в першій частині питання, вивчимо відповідні методи класів Pattern, Matcher і PatternSyntaxException. Ви також познайомитеся з двома утилітами, що використовують Регулярні вирази для спрощення рішення поширених завдань програмування. Перша з них витягує коментарі з коду для документації. Друга являє собою бібліотеку багаторазового коду, призначену для виконання лексичного аналізу - істотний компонент ассемблеров, компіляторів і тому подібного програмного забезпечення.

ЗАВАНТАЖЕННЯ ВИХІДНОГО КОДА

Отримати весь вихідний код (створений Джеффом Фрізеном для сайту JavaWorld) демо-додатків з даної статті можна звідси .

Вивчаємо API Regex

Pattern, Matcher і PatternSyntaxException - три класи, що становлять API Regex. Кожен з них надає методи, що дозволяють використовувати регулярні вирази в вашому коді.

Методи класу Pattern

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

  • Pattern compile (String regex) компілює вміст regex в проміжне представлення, яке зберігається в новому об'єкті Pattern. Цей метод або повертає посилання на об'єкт, в разі успішного виконання, або генерує виняток PatternSyntaxException в разі виявлення некоректного синтаксису регулярного виразу. Будь-який об'єкт класу Matcher, який використовується цим об'єктом Pattern або повертається з нього, використовує його налаштування за замовчуванням, наприклад, пошук з урахуванням регістру. Як приклад, фрагмент коду Pattern p = Pattern.compile ( "(? M) ^ \\."); створює об'єкт Pattern, який зберігає скомпільований уявлення регулярного виразу для пошуку рядків, що починаються з крапки.

  • Pattern compile (String regex, int flags) вирішує ту ж задачу, що і Pattern compile (String regex), але з урахуванням flags: набору бітових констант для побітових прапорів типу АБО. У класі Pattern оголошені константи CANON_EQ, CASE_INSENSITIVE, COMMENTS, DOTALL, LITERAL, MULTILINE, UNICODE_CASE, UNICODE_CHARACTER_CLASS і UNIX_LINES, які можна комбінувати за допомогою побітового АБО (наприклад, CASE_INSENSITIVE | DOTALL) і передати в аргументі flags.

  • За винятком CANON_EQ, LITERAL і UNICODE_CHARACTER_CLASS, ці константи є альтернативою вкладених прапоровим виразами, продемонстрованим у частині 1. При виявленні флагової константи, що відрізняється від визначених у класі Pattern, метод Pattern compile (String regex, int flags) генерує виняток java.lang.IllegalArgumentException . Наприклад, Pattern p = Pattern.compile ( "^ \\.", Pattern.MULTILINE); еквівалентно попередньому прикладу, причому константа Pattern.MULTILINEі вкладене флаговая вираз (? M) роблять одне і те ж.

Іноді буває необхідно отримати копію початкового рядка регулярного виразу, скомпільованої в об'єкт Pattern, разом з використовуваними їм прапорами. Для цього можна викликати такі методи:

  • String pattern () повертає вихідну рядок регулярного виразу, скомпільовану в об'єкт Pattern.

  • int flags () повертає прапори об'єкта Pattern.

Після отримання об'єкта Pattern, він зазвичай використовується для отримання об'єкта Matcher, для виконання операцій пошуку за шаблоном. Метод Matcher matcher (Charsequence input) створює об'єкт Matcher, шукає в тексті input відповідність шаблону об'єкта Pattern. При виклику він повертає посилання на цей об'єкт Matcher. Наприклад, команда Matcher m = p.matcher (args [1]); повертає Matcher для об'єкта Pattern, на який посилається змінна p. Одноразовий пошук Метод static boolean matches (String regex, CharSequence input) класу Pattern дозволяє заощадити на створенні об'єктів Pattern і Matcher при одноразовому пошуку за шаблоном. Цей метод повертає true, якщо в input знаходиться відповідність шаблону regex, в іншому випадку він повертає false. Якщо в регулярному виразі міститься синтаксична помилка, метод генерує виняток PatternSyntaxException. Наприклад, System.out.println (Pattern.matches ( "[az [\\ s]] *", "all lowercase letters and whitespace only")); виводить true, підтверджуючи, що у фразі all lowercase letters and whitespace only містяться тільки прогалини і символи в нижньому регістрі.

розбиття тексту

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

  • Метод String [] split (CharSequence text, int limit) розбиває text відповідно до знайдених відповідниками шаблоном об'єкта Pattern і повертає результати в масиві. Кожен елемент масиву задає текстову послідовність, відокремлену від наступній послідовності відповідним шаблоном фрагментом тексту (або кінцем тексту). Елементи масиву знаходяться в тому ж порядку, в якому вони зустрічаються в text.

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

    • При позитивному значенні виконується пошук не більше ніж limit-1 відповідностей, а довжина масиву не перевищує limit елементів.

    • При від'ємному значенні виконується пошук всіх можливих відповідностей, і довжина масиву може бути довільною.

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

  • Метод String [] split (CharSequence text) викликає попередній метод з 0 в якості аргументу limit і повертає результат його виклику.

Нижче наведені результати роботи методу split (CharSequence text) за рішенням завдання розщеплення облікового запису співробітника на окремі поля імені, віку, поштової адреси і зарплати: Pattern p = Pattern. compile ( ", \\ s"); String [] fields = p. split ( "John Doe, 47, Hillsboro Road, 32000"); for (int i = 0; i <fields .length; i ++) System .out. println (fields [i]); У наведеному вище коді описано регулярний вираз для пошуку знака коми, за яким безпосередньо слід одиночний символ пропуску. Ось результати його виконання: John Doe 47 Hillsboro Road 32000 Предикати шаблонів і API StreamsВ Java 8 в класі Pattern з'явився метод Predicate asPredicate (). Цей метод створює предикат (функцію з булевим значенням), який використовується для пошуку за шаблоном. Використання цього методу показано в наступному фрагменті коду: List progLangs = Arrays. asList ( "apl", "basic", "c", "c ++", "c #", "cobol", "java", "javascript", "perl", "python", "scala"); Pattern p = Pattern. compile ( "^ c"); progLangs. stream (). filter (p. asPredicate ()). forEach (System .out:: println); Цей код створює список назв мов програмування, потім компілює шаблон для пошуку всіх назв, що починаються з літери c. Остання з вищенаведених рядків коду реалізує отримання послідовного потоку даних з цим списком в якості джерела. Він встановлює фільтр, який використовує булеву функцію asPredicate (), яка повертає true, коли назва починається з букви c і виконує ітерацію по потоку, виводячи відповідні назви в стандартний потік виведення. Ця остання рядок еквівалентна наступного звичайному циклу, знайомому вам за додатком RegexDemo з частини 1: for (String progLang: progLangs) if (p. Matcher (progLang). Find ()) System .out. println (progLang);

Методи класу Matcher

Примірник класу Matcher описує механізм виконання операцій пошуку за шаблоном в послідовності символів шляхом інтерпретації скомпільованої регулярного виразу класу Pattern. Об'єкти класу Matcher підтримують різні види операцій пошуку за шаблоном:

  • Метод boolean find () шукає у вхідному тексті наступне збіг. Цей метод починає перегляд або на початку заданого тексту, або на першому символі після попереднього збігу. Другий варіант можливий тільки якщо попередній виклик цього методу повернув true і сопоставітель ні скинутий. У будь-якому випадку, в разі успішного пошуку повертається логічне значення true. Приклад цього методу ви можете знайти в RegexDemo з частини 1.

  • Метод boolean find (int start) скидає сопоставітель і шукає в тексті наступне збіг. Перегляд починається з позиції, що задається параметром start. У разі успішного пошуку повертається логічне значення true. Наприклад, m.find (1); переглядає текст, починаючи з позиції 1 (позиція 0 ігнорується). Якщо параметр start містить від'ємне значення або значення, що перевищує довжину тексту сопоставітеля, метод генерує виняток java.lang.IndexOutOfBoundsException.

  • Метод boolean matches () намагається зіставити з шаблоном весь текст. Він повертає логічне значення true, якщо весь текст відповідає шаблону. Наприклад, код Pattern p = Pattern.compile ( "\\ w *"); Matcher m = p.matcher ( "abc!"); System.out.println (p.matches ()); виводить false, оскільки символ! не є словоутворюючим символом.

  • Метод boolean lookingAt () намагається зіставити з шаблоном заданий текст. Цей метод повертає true, якщо будь-яка частина тексту відповідає шаблоном. На відміну від методу matches () ;, весь текст не повинен відповідати шаблоном. Наприклад, Pattern p = Pattern.compile ( "\\ w *"); Matcher m = p.matcher ( "abc!"); System.out.println (p.lookingAt ()); виведе true, оскільки початок тексту abc! складається тільки з словообразующих символів.

На відміну від об'єктів класу Pattern, об'єкти класу Matcher зберігають інформацію про стан. Іноді може знадобитися скинути сопоставітель, щоб очистити цю інформацію після закінчення пошуку за шаблоном. Для скидання сопоставітеля існують такі методи:

  • Метод Matcher reset () скидає стан сопоставітеля, включаючи позицію для додавання в кінець (скидається в 0). Наступна операція пошуку за шаблоном починається на початку тексту сопоставітеля. Повертається посилання на поточний об'єкт Matcher. Наприклад, m.reset (); скидає сопоставітель, на який посилається m.

  • Метод Matcher reset (CharSequence text) скидає стан сопоставітеля і задає новий текст сопоставітеля, рівний text. Наступна операція пошуку за шаблоном починається на початку нового тексту сопоставітеля. Повертається посилання на поточний об'єкт Matcher. Наприклад, m.reset ( "new text"); скидає сопоставітель, на який посилається m і задає в якості нового тексту сопоставітеля значення "new text".

Додавання тексту в кінець

Позиція сопоставітеля для додавання в кінець задає початок тексту сопоставітеля, який додається в кінець об'єкта типу java.lang.StringBuffer. Цю позицію використовують такі методи:

  • Метод Matcher appendReplacement (StringBuffer sb, String replacement) читає символи тексту сопоставітеля і приєднує їх в кінець об'єкта StringBuffer, на який посилається аргумент sb. Цей метод припиняє читання на останньому символі, що передує попередньому відповідності шаблоном. Далі, метод додає символи з об'єкта типу String, на який посилається аргумент replacement, в кінець об'єкта StringBuffer (рядок replacement може містити посилання на текстові послідовності, захоплені під час попереднього пошуку; вони вказуються за допомогою символів ($) і номерів захоплюваних груп). Нарешті, метод встановлює значення позиції сопоставітеля для додавання в кінець рівним позиції останнього співпала символу плюс одиниця, після чого повертає посилання на поточний сопоставітель.

  • Метод Matcher appendReplacement (StringBuffer sb, String replacement) генерує виняток java.lang.IllegalStateException, якщо сопоставітель ще не знаходив відповідності або попередня спроба пошуку завершилася невдало. Він генерує виняток IndexOutOfBoundsException, якщо рядок replacement задає відсутню в шаблоні захоплювану групу).


  • Метод StringBuffer appendTail (StringBuffer sb) додає весь текст в об'єкт StringBuffer і повертає посилання на цей об'єкт. Після останнього виклику методу appendReplacement (StringBuffer sb, String replacement), викличте метод appendTail (StringBuffer sb), щоб скопіювати текст, що залишився в об'єкт StringBuffer.

Захоплювані групи Як ви пам'ятаєте з частини 1, захоплююча група - це послідовність символів, укладена в метасимволу круглих дужок (()). Мета цієї конструкції полягає в збереженні знайдених символів для подальшого повторного використання під час пошуку за шаблоном. Всі символи з захоплюваної групи розглядаються під час пошуку за шаблоном як єдине ціле. У наступному коді виконується виклик методів appendReplacement (StringBuffer sb, String replacement) і appendTail (StringBuffer sb для заміни в початковому тексті всіх входжень послідовності символів cat на caterpillar: Pattern p = Pattern. Compile ( "(cat)"); Matcher m = p . matcher ( "one cat, two cats, or three cats on a fence"); StringBuffer sb = new StringBuffer (); while (m. find ()) m. appendReplacement (sb, "$ 1erpillar"); m. appendTail (sb); System .out. println (sb); Використання захоплюваної групи і посилання на неї в заміщає тексті вказує програмі вставляти erpillar після кожного входження cat. Результат виконання даного коду виглядає наступним чином: one caterpillar, two caterpill ars, or three caterpillars on a fence

заміна тексту

Клас Matcher надає нам два методи для текстової заміни, доповнюють метод appendReplacement (StringBuffer sb, String replacement). За допомогою цих методів можна замінювати або перше входження [заміщає тексту] або все входження:

  • Метод String replaceFirst (String replacement) скидає сопоставітель, створює новий об'єкт String, копіює в цей рядок всі символи тексту сопоставітеля (аж до першого збігу), додає в її кінець символи з replacement, копіює в рядок залишилися символи і повертає об'єкт String (в рядку replacement можна вказувати посилання на захоплені під час попереднього пошуку текстові послідовності, за допомогою символів долара і номерів захоплюваних груп).

  • Метод String replaceAll (String replacement) діє аналогічно методу String replaceFirst (String replacement), але замінює символами з рядка replacement все знайдені збіги.

Регулярний вираз \ s + служить для пошуку одного або більше пробільних символів у вхідному тексті. Нижче, ми скористаємося цим регулярним виразом і викличемо метод replaceAll (String replacement) для видалення дубльованих прогалин: Pattern p = Pattern. compile ( "\\ s +"); Matcher m = p. matcher ( "Видаляємо \ t \ t зайві прогалини."); System .out. println (m. replaceAll ( "")); Ось результати: Видаляємо зайві прогалини. Регулярні вирази в Java, частина 4 Регулярні вирази в Java, частина 5