matched: <b>bold text</b> part 1: <b> part 2: bold text part 3: </b> matched: <a href=howdy.html>click me</a> part 1: <a href=howdy.html> part 2: click me part 3: </a>
| « Java аннотации. Пример 1 | Системы управления версиями для программистов и не только. Часть 2 » |
Regexp-ы для java точь в точь как для php
Сегодня я столкнулся с необходимость написать несколько регулярных выражений для java. Надо сказать, что последнее время я часто переключаюсь между java и php, так что держать в голове два стиля использования regexp-ов становится все труднее: допускаю мелкие ошибки и опечатки. Отличия в regexp-ах не в самом синтаксисе (он обычный, те же самый \w, \d, классы символов и их модификаторы). Отличия в мелочах и эти мелочи мне не нравятся:Сравнение java-подхода к regexp-ам и подхода php
Например, для того чтобы в java проверить строку на наличие определенного шаблона я должен сделать так:
System.out.println ( "boy goes to school".matches("boy") );
System.out.println ( "boy goes to school".matches(".*boy.*") );
// The "i" after the pattern delimiter indicates a case-insensitive searchif (preg_match("/php/i", "PHP is the web scripting language of choice.")) {
echo "A match was found.";
}else {
echo "A match was not found.";
}
В java синтаксис указания модификаторов другой:
"boy goes to school".matches("(?i).*BOY.*")
// The \\2 is an example of backreferencing. This tells pcre that// it must match the second set of parentheses in the regular expression// itself, which would be the ([\w]+) in this case. The extra backslash is// required because the string is in double quotes.$html = "<b>bold text</b><a href=howdy.html>click me</a>";
preg_match_all("/(<([\w]+)[^>]*>)(.*)(<\/\\2>)/", $html, $matches, PREG_SET_ORDER);
foreach ($matches as $val) {
echo "matched: " . $val[0] . "\n";
echo "part 1: " . $val[1] . "\n";
echo "part 2: " . $val[3] . "\n";
echo "part 3: " . $val[4] . "\n\n";
}
Pattern p = null;
Matcher m = null;
try {
// компилируем regexp, который ищет все комментарии без учета их длиныp = Pattern.compile("(?is)/\\*.*?\\*/");
m = p.matcher(sin);
// создаем объект Matcher, с помощью которого затем будет организован// цикл перебора всех найденных (подошедших под шаблон) строк-коментариев.sb = new StringBuffer();
while (m.find()) {
// функция find ищет в исходной строке очередной подошедший для regexp-а фрагмент,// но как только таких совпадений больше нет, то цикл будет прекращенtry {
String gr_0 = m.group(0);
// все группы (части regexp-а заключенные в круглые скобки) могут быть доступны// с помощью функции group(номер_группы).// Есть особый номер группы – 0 – эта группа захватывает абсолютно весь текст строки,// который проассоциировался с регулярным выражениемif (gr_0.length() - 4 <= pi)
// проверяем, что если длина этого комментария за вычетом четырех символов//(два знака “*” и два знака “/”) все же не смогла превзойти предельную то в буфер sb помещается комментарийm.appendReplacement(sb, gr_0);
//else// иначе комментарий удалется// m.appendReplacement(sb, "");} catch (Exception e) {
e.printStackTrace();
}}// end of -- while --
// завершаем обработку хвоста исходной строки – после последнего совпадения с regexpm.appendTail(sb);
} catch (Exception e) {
e.printStackTrace();
}sin = sb.toString();
Ближе к делу
Одним словом, в java сделать можно больше чем в php но всегда большим количеством кода. Так что я решил написать небольшой класс-утилитку которая бы позволяла вызывать regexp-ы сходным образом, как для php:
Вот пример использования:
/*Для записи регулярного выражения используется Perl-like синтаксис, когда строка состоит из двух секцийРАЗДЕЛИТЕЛЬ_1 СЕКЦИЯ_ЧТО_ИСКАТЬ РАЗДЕЛИТЕЛЬ_2 СЕКЦИЯ_МОДИФИКАТОРЫразделители строятся по стандартным правилам и могут быть любыми одинаковыми символами, или парными[] и {} и () и <>секция модификаторов может состоять из следующих символов:i - поиск/замена будет регистронечувствительной CASE_INSENSITIVEd - модификатор UNIX_LINESx - режим когда игнорируются пробелы и можно комментировать выражение COMMENTSm - многострочный режим, в котором символы ^ и $ ассоциируются с началом и концом каждой из строк MULTILINEs - режим "точка - это все", в котором символ новой строки ассоциирутся с символом "." DOTALLu - включена поддержка Unicode case-folding-га UNICODE_CASE*/List<String> rez = new ArrayList<String>();
List<List<String>> rez2 = new ArrayList<List<String>>();
String pattern = "/(\\w+)-(\\w+)/idxmsu";
String inputString = "hello-petyano petka-lenka";
// пример с поиском внутри строки всех совпаденийif (RegexpUtils.preg_match_all(pattern, inputString, rez2)) {
System.out.println("rez = " + rez);
}// ищем одно, только первое, совпадениеif (RegexpUtils.preg_match("|(\\w+)-(\\w+)|", inputString, rez)) {
System.out.println("rez = " + rez);
}System.out.println("=============================== ");
// теперь пример простой заменыSystem.out.println("rez = " + RegexpUtils.preg_replace("/-(\\w+)/i", inputString, "X:$1"));
// и пример сложной заменыSystem.out.println("rez = " + RegexpUtils.preg_replace_callback("/-(\\w+)/i", inputString,
new RegexpUtils.Replacer() {
public String onMatch(List<String> matches) {
return "z:" + matches.get(1);
}}));
rez = [[hello-petyano, hello, petyano], [ petka-lenka, petka, lenka]] rez = [hello-petyano petka-lenka, hello, petyano] =============================== rez = helloX:petyano petkaX:lenka rez = helloz:petyano petkaz:lenka
А вот пример исходных кодов моей библиотечки
package testi.catsandusers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*** Класс-обертка над стандартными для java средствами работы с регулярными выражениями* Вместо классов Pattern, Matcher и циклов используются функции и подход их использования аналогичный php*/public class RegexpUtils {
/*** Интерфейс который должен реализовать тот кто хочет выполнить обработку, замену каждого вхождения программно*/public static interface Replacer {
/*** Метод должен вернуть строку на которую будет выполнена замена найденного regexp-ом фрагмента** @param matches список с информацией об найденном фрагменте, нулевой элемент списка* содержит весь текст "совпадения"* остальные же элементы 1,2, ... содержат значения для групп внутри регулярного выражения* @return*/public String onMatch(List<String> matches);
}/*** Кэш, в котором хранятся скомпилированные regexp-выражения*/private static HashMap<String, Pattern> cache = new HashMap<String, Pattern>();
/*** Очиска кэша скомпилированных regexp-выражений*/public void clearCache() {
cache.clear();
}/*** Выполнить поиск в строке шаблона и заменить его на новую величину вычисляемую динамически, пользователем** @param pattern шаблон (regexp)* @param input строка, где выполнить поиск* @param by объект Replacer - задает значение на что выполнить замену* @return строка после замены*/public static String preg_replace_callback(String pattern, String input, Replacer by) {
Pattern p = compile(pattern, false);
Matcher m = p.matcher(input);
final int gcount = m.groupCount();
StringBuffer sb = new StringBuffer();
ArrayList<String> row = new ArrayList<String>();
while (m.find()) {
try {
row.clear();
for (int i = 0; i <= gcount; i++)
row.add(m.group(i));
m.appendReplacement(sb, by.onMatch(row));
} catch (Exception e) {
e.printStackTrace();
}}//end -- while --
m.appendTail(sb);
return sb.toString();
}/*** Выполнить поиск в строке шаблона и заменить его на новую величину вычисляемую средствами Regexp-выражения* @param pattern шаблон (regexp)* @param input строка, где выполнить поиск* @param by строка, на которую нужно заменить найденное значение* @return строка после замены*/public static String preg_replace(String pattern, String input, String by) {
Pattern p = compile(pattern, false);
Matcher m = p.matcher(input);
StringBuffer sb = new StringBuffer();
while (m.find()) {
try {
m.appendReplacement(sb, by);
} catch (Exception e) {
e.printStackTrace();
}}//end -- while --
m.appendTail(sb);
return sb.toString();
}/*** Проверка того ассоциирутся ли строка с шаблоном* @param pattern шаблон (regexp)* @param input строка, где выполнить поиск* @param rez Список куда будет помещена информация об совпадении:* нулевой элемент списка содержит весь текст совпадения* 1, 2, ... содержат значения групп* @return булево выражение - признак того что ассоциация произошла*/public static boolean preg_match(String pattern, String input, List <String> rez) {
Pattern p = compile(pattern, true);
Matcher m = p.matcher(input);
final int gcount = m.groupCount();
if (rez != null)
rez.clear();
if (m.matches())
for (int i = 0; i <= gcount; i++) {
if (rez != null)
rez.add(m.group(i));
}return rez.size() > 0;
}/*** Проверка того что в строке содержится некоторый шаблон и возвращается список со всеми найденными группами совпадений* @param pattern шаблон (regexp)* @param input строка, где выполнить поиск* @param rez список, куда будут помещены все найденные соответвия, список двухуровневый: первый уровень* содержит перечисление объектов-списков, каждый из которых содержит информацию об* очередном совпадении в таком же формате как и метод preg_match* @return*/public static boolean preg_match_all(String pattern, String input, List <List <String>> rez) {
Pattern p = compile(pattern, true);
Matcher m = p.matcher(input);
final int gcount = m.groupCount();
if (rez != null)
rez.clear();
while (m.find()) {
ArrayList row = new ArrayList();
for (int i = 0; i <= gcount; i++) {
if (rez != null)
row.add(m.group(i));
}if (rez != null)
rez.add(row);
}return rez.size() > 0;
}/*** Слежебный метод выполняющий компиляцию regexp-а и сохранение его в кэш* @param pattern текст регулярного выражения* @param surroundBy признак того нужно ли выражение окружить .*?* @return скомпилированный Pattern*/private static Pattern compile(String pattern, boolean surroundBy) {
if (cache.containsKey(pattern)) return cache.get(pattern);
final String pattern_orig = pattern;
final char firstChar = pattern.charAt(0);
char endChar = firstChar;
if (firstChar == '(') endChar = '}';
if (firstChar == '[') endChar = ']';
if (firstChar == '{') endChar = '}';
if (firstChar == '<') endChar = '>';
int lastPos = pattern.lastIndexOf(endChar);
if (lastPos == -1)
throw new RuntimeException("Invalid pattern: " + pattern);
char[] modifiers = pattern.substring(lastPos + 1).toCharArray();
int mod = 0;
for (int i = 0; i < modifiers.length; i++) {
char modifier = modifiers[i];
switch (modifier) {
case 'i':
mod |= Pattern.CASE_INSENSITIVE;
break;
case 'd':
mod |= Pattern.UNIX_LINES;
break;
case 'x':
mod |= Pattern.COMMENTS;
break;
case 'm':
mod |= Pattern.MULTILINE;
break;
case 's':
mod |= Pattern.DOTALL;
break;
case 'u':
mod |= Pattern.UNICODE_CASE;
break;
}}pattern = pattern.substring(1, lastPos);
if (surroundBy) {
if (pattern.charAt(0) != '^')
pattern = ".*?" + pattern;
if (pattern.charAt(pattern.length() - 1) != '$')
pattern = pattern + ".*?";
}final Pattern rezPattern = Pattern.compile(pattern, mod);
cache.put(pattern_orig, rezPattern);
return rezPattern;
}}
| « Java аннотации. Пример 1 | Системы управления версиями для программистов и не только. Часть 2 » |