Регулярні вирази в 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("[a-z[\\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може містити посилання на текстові послідовності, захоплені під час попереднього пошуку; вони вказуються за допомогою символів($)і номерів груп, що захоплюються). Нарешті, метод встановлює значення позиції співставника для додавання в кінець рівним позиції останнього символу, що збігся плюс одиниця, після чого повертає посилання на поточний зіставник. -
Метод
StringBuffer appendTail(StringBuffer sb)додає весь текст до об'єктаStringBufferі повертає посилання на цей об'єкт. Після останнього виклику методуappendReplacement(StringBuffer sb, String replacement), викличте методappendTail(StringBuffer sb), щоб скопіювати текст, що залишився в об'єктStringBuffer.
Метод Matcher appendReplacement(StringBuffer sb, String replacement)генерує виняток java.lang.IllegalStateException, якщо співставник ще не знаходив відповідності або попередня спроба пошуку завершилася невдало. Він генерує виняток IndexOutOfBoundsException, якщо рядок replacementзадає відсутню в шаблоні групу, що захоплюється).
| Захоплювані групи |
|---|
Як ви пам'ятаєте з частини 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 caterpillars, 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
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ