- Home »

Регулярные выражения в Java — пример и руководство
Если ты работаешь с логами, парсишь конфиги или автоматизируешь задачи на сервере, то рано или поздно столкнешься с необходимостью найти и извлечь нужную информацию из текста. И тут на помощь приходят регулярные выражения — мощный инструмент для работы с текстом. В Java они реализованы через классы Pattern и Matcher, которые позволяют не только искать совпадения, но и валидировать данные, парсить логи и даже генерировать отчеты.
Эта статья поможет тебе разобраться с regex в Java на практических примерах, которые реально пригодятся в админской работе. Мы рассмотрим, как быстро настроить поиск по логам, валидацию IP-адресов, парсинг конфигов и многое другое. Никакой теории ради теории — только то, что действительно работает в боевых условиях.
Как это работает: основы regex в Java
В Java для работы с регулярными выражениями используются два основных класса из пакета java.util.regex:
- Pattern — компилированное регулярное выражение
- Matcher — движок для поиска совпадений в тексте
Базовый пример использования выглядит так:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexExample {
public static void main(String[] args) {
String text = "192.168.1.1 - GET /index.html 200";
Pattern pattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
System.out.println("Найден IP: " + matcher.group());
}
}
}
Основные методы, которые тебе понадобятся:
- find() — найти следующее совпадение
- matches() — проверить, совпадает ли вся строка с паттерном
- group() — получить найденную подстроку
- replaceAll() — заменить все совпадения
Быстрая настройка: пошаговое руководство
Давайте разберем настройку на конкретном примере — парсинг логов Apache. Это одна из самых частых задач, с которой сталкиваются админы.
Шаг 1: Создай класс для парсинга логов
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class LogParser {
// Паттерн для Common Log Format
private static final String LOG_PATTERN =
"^([\\d.]+) (\\S+) (\\S+) \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(.+?)\" (\\d{3}) (\\d+)";
private static final Pattern pattern = Pattern.compile(LOG_PATTERN);
public static void parseLog(String logFile) {
try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
System.out.println("IP: " + matcher.group(1));
System.out.println("Время: " + matcher.group(4));
System.out.println("Запрос: " + matcher.group(5));
System.out.println("Код ответа: " + matcher.group(6));
System.out.println("Размер: " + matcher.group(7));
System.out.println("---");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Шаг 2: Добавь валидацию и фильтрацию
public class LogAnalyzer {
private static final Pattern IP_PATTERN =
Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
private static final Pattern ERROR_PATTERN =
Pattern.compile("\"[^\"]*\" ([4-5]\\d{2})");
public static boolean isValidIP(String ip) {
return IP_PATTERN.matcher(ip).matches();
}
public static void findErrors(String logFile) {
try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = ERROR_PATTERN.matcher(line);
if (matcher.find()) {
System.out.println("Ошибка " + matcher.group(1) + ": " + line);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Шаг 3: Создай утилиту для мониторинга
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class LogMonitor {
private Map ipCounts = new HashMap<>();
private Map errorCounts = new HashMap<>();
private static final Pattern ACCESS_PATTERN =
Pattern.compile("^([\\d.]+).*\"[^\"]*\" (\\d{3})");
public void processLogLine(String line) {
Matcher matcher = ACCESS_PATTERN.matcher(line);
if (matcher.find()) {
String ip = matcher.group(1);
String code = matcher.group(2);
ipCounts.computeIfAbsent(ip, k -> new AtomicInteger(0)).incrementAndGet();
if (code.startsWith("4") || code.startsWith("5")) {
errorCounts.computeIfAbsent(ip, k -> new AtomicInteger(0)).incrementAndGet();
}
}
}
public void printTopIPs(int limit) {
ipCounts.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
(a, b) -> b.get() - a.get()))
.limit(limit)
.forEach(entry ->
System.out.println(entry.getKey() + ": " + entry.getValue().get()));
}
}
Практические примеры и кейсы
Вот несколько готовых решений для типичных задач:
Парсинг конфигурационных файлов
public class ConfigParser {
// Парсинг nginx.conf
private static final Pattern NGINX_DIRECTIVE =
Pattern.compile("^\\s*(\\w+)\\s+([^;]+);");
// Парсинг Apache VirtualHost
private static final Pattern VHOST_PATTERN =
Pattern.compile("]+)>");
// Парсинг переменных окружения
private static final Pattern ENV_PATTERN =
Pattern.compile("^([A-Z_]+)=(.*)$");
public static Map parseEnvFile(String content) {
Map env = new HashMap<>();
Pattern pattern = Pattern.compile("^([A-Z_][A-Z0-9_]*)=(.*)$", Pattern.MULTILINE);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
String key = matcher.group(1);
String value = matcher.group(2).replaceAll("^[\"']|[\"']$", "");
env.put(key, value);
}
return env;
}
}
Валидация и очистка данных
public class DataValidator {
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$");
private static final Pattern DOMAIN_PATTERN =
Pattern.compile("^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+" +
"[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", Pattern.CASE_INSENSITIVE);
private static final Pattern SQL_INJECTION_PATTERN =
Pattern.compile("('|(\\-\\-)|(;)|(\\|)|(\\*)|(%))", Pattern.CASE_INSENSITIVE);
public static boolean isValidEmail(String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
public static boolean isValidDomain(String domain) {
return DOMAIN_PATTERN.matcher(domain).matches();
}
public static String sanitizeInput(String input) {
return input.replaceAll("[<>\"'&]", "");
}
public static boolean containsSQLInjection(String input) {
return SQL_INJECTION_PATTERN.matcher(input).find();
}
}
Мониторинг и алерты
public class SecurityMonitor {
private static final Pattern BRUTE_FORCE_PATTERN =
Pattern.compile("Failed password for .* from ([\\d.]+)");
private static final Pattern SUSPICIOUS_UA_PATTERN =
Pattern.compile("\"(.*(?:bot|crawler|spider|scraper).*?)\"", Pattern.CASE_INSENSITIVE);
private static final Pattern DDoS_PATTERN =
Pattern.compile("^([\\d.]+).*\" (\\d{3}) \\d+$");
public void checkBruteForce(String logLine) {
Matcher matcher = BRUTE_FORCE_PATTERN.matcher(logLine);
if (matcher.find()) {
String ip = matcher.group(1);
System.out.println("Возможная атака брутфорса с IP: " + ip);
// Здесь можно добавить логику блокировки
}
}
public void detectSuspiciousActivity(String logLine) {
Matcher matcher = SUSPICIOUS_UA_PATTERN.matcher(logLine);
if (matcher.find()) {
String userAgent = matcher.group(1);
System.out.println("Подозрительный User-Agent: " + userAgent);
}
}
}
Сравнение производительности
Важно понимать, что регулярные выражения могут быть медленными. Вот сравнение различных подходов:
Метод | Скорость | Память | Гибкость | Когда использовать |
---|---|---|---|---|
String.contains() | Очень быстро | Мало | Низкая | Простой поиск подстроки |
String.indexOf() | Быстро | Мало | Низкая | Поиск позиции |
Pattern.matches() | Средне | Средне | Высокая | Валидация |
Matcher.find() | Медленно | Много | Очень высокая | Сложный поиск |
Оптимизация и лучшие практики
Несколько советов для повышения производительности:
- Компилируй паттерны заранее — Pattern.compile() — дорогая операция
- Используй флаги — Pattern.CASE_INSENSITIVE, Pattern.MULTILINE, Pattern.DOTALL
- Избегай жадных квантификаторов — используй .*? вместо .*
- Кэшируй результаты — особенно для валидации
public class OptimizedRegex {
// Плохо - компилируется каждый раз
public boolean validateEmailBad(String email) {
return Pattern.matches("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$", email);
}
// Хорошо - компилируется один раз
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$");
public boolean validateEmailGood(String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
// Еще лучше - с кэшем
private Map emailCache = new ConcurrentHashMap<>();
public boolean validateEmailCached(String email) {
return emailCache.computeIfAbsent(email,
e -> EMAIL_PATTERN.matcher(e).matches());
}
}
Интеграция с другими инструментами
Регулярные выражения отлично работают в связке с другими инструментами:
Logback и slf4j
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
import java.util.regex.Pattern;
public class RegexFilter extends Filter {
private Pattern pattern;
public void setPattern(String regex) {
this.pattern = Pattern.compile(regex);
}
@Override
public FilterReply decide(ILoggingEvent event) {
if (pattern.matcher(event.getFormattedMessage()).find()) {
return FilterReply.ACCEPT;
}
return FilterReply.DENY;
}
}
Apache Commons и Stream API
import java.util.stream.Stream;
import java.nio.file.Files;
import java.nio.file.Paths;
public class StreamRegex {
public static void analyzeLogFile(String filename) throws IOException {
Pattern errorPattern = Pattern.compile("ERROR|FATAL");
Files.lines(Paths.get(filename))
.filter(line -> errorPattern.matcher(line).find())
.map(line -> line.split("\\s+"))
.filter(parts -> parts.length > 3)
.forEach(parts -> System.out.println("Ошибка: " + parts[3]));
}
}
Альтернативные решения
Хотя Java regex мощный инструмент, иногда стоит рассмотреть альтернативы:
- Apache Commons Lang — StringUtils для простых операций
- Google Guava — Splitter для разделения строк
- ANTLR — для сложного парсинга
- Jackson — для JSON/XML
Ссылки на полезные ресурсы:
Автоматизация и скрипты
Регулярные выражения открывают массу возможностей для автоматизации:
// Скрипт для автоматической очистки логов
public class LogRotator {
private static final Pattern LOG_DATE_PATTERN =
Pattern.compile("(\\d{4}-\\d{2}-\\d{2})");
public static void cleanOldLogs(String logDir, int daysToKeep) {
try {
LocalDate cutoffDate = LocalDate.now().minusDays(daysToKeep);
Files.walk(Paths.get(logDir))
.filter(Files::isRegularFile)
.filter(path -> {
String filename = path.getFileName().toString();
Matcher matcher = LOG_DATE_PATTERN.matcher(filename);
if (matcher.find()) {
LocalDate logDate = LocalDate.parse(matcher.group(1));
return logDate.isBefore(cutoffDate);
}
return false;
})
.forEach(path -> {
try {
Files.delete(path);
System.out.println("Удален старый лог: " + path);
} catch (IOException e) {
e.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
Для работы с большими объемами данных рекомендую использовать VPS с достаточным объемом RAM, а для серьезных нагрузок — выделенный сервер.
Интересные факты и нестандартные применения
Несколько неочевидных способов использования regex:
- Генерация паролей — можно использовать для валидации сложности
- Парсинг CSV с экранированием — сложно, но возможно
- Извлечение цветов из CSS — полезно для тем оформления
- Поиск уязвимостей — сканирование кода на небезопасные конструкции
// Нестандартный пример - парсинг CSS
public class CSSParser {
private static final Pattern CSS_RULE_PATTERN =
Pattern.compile("([^{]+)\\{([^}]+)\\}");
private static final Pattern CSS_PROPERTY_PATTERN =
Pattern.compile("([^:]+):\\s*([^;]+);?");
public static Map> parseCSS(String css) {
Map> result = new HashMap<>();
Matcher ruleMatcher = CSS_RULE_PATTERN.matcher(css);
while (ruleMatcher.find()) {
String selector = ruleMatcher.group(1).trim();
String properties = ruleMatcher.group(2);
Map props = new HashMap<>();
Matcher propMatcher = CSS_PROPERTY_PATTERN.matcher(properties);
while (propMatcher.find()) {
props.put(propMatcher.group(1).trim(), propMatcher.group(2).trim());
}
result.put(selector, props);
}
return result;
}
}
Заключение и рекомендации
Регулярные выражения в Java — это мощный инструмент, который должен быть в арсенале каждого админа и разработчика. Они незаменимы для:
- Парсинга логов — быстрый анализ и мониторинг
- Валидации данных — проверка форматов и безопасности
- Автоматизации — обработка конфигов и скриптов
- Мониторинга безопасности — поиск подозрительной активности
Главные принципы использования:
- Компилируй паттерны заранее и переиспользуй
- Тестируй регулярки на больших объемах данных
- Используй группы для извлечения нужных частей
- Не забывай про экранирование спецсимволов
- Кэшируй результаты для часто вызываемых операций
Помни: regex — это не серебряная пуля. Для простых задач часто достаточно String.contains() или String.indexOf(). Но когда нужна гибкость и мощность, регулярные выражения показывают себя с лучшей стороны. Особенно это актуально при работе с серверными логами, где объемы данных могут быть огромными, а требования к скорости обработки — высокими.
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.