Спрощуємо вирішення найпоширеніших завдань програмування за допомогою 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
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ