Java 正则表达式:比你想象的更强大
正则表达式是每一位开发者都应该掌握的技能,但你知道吗,在 Java 中,正则表达式的处理能力远超过你的想象。今天,我们不单纯讲解正则表达式的语法,而是通过具体的编码实验,对比不同正则表达式方案的性能和功能差异,帮助你深刻理解 Java 正则表达式的强大之处。
实验 1:简单匹配
假设你需要验证一个字符串是否包含特定的子字符串,比如检查一个 URL 是否包含 ".com"。你会选择什么样的方法呢?让我们看看两种方案的对比。
方案 1:使用 String.contains() 方法
java
public class TestContains {
public static void main(String[] args) {
String url = "https://www.example.com";
boolean contains = url.contains(".com"); // 简单的子字符串匹配
System.out.println("Contains .com? " + contains);
}
}
方案 2:使用正则表达式 Pattern 和 Matcher
java
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class TestRegex {
public static void main(String[] args) {
String url = "https://www.example.com";
Pattern pattern = Pattern.compile("\\.com"); // 使用正则表达式
Matcher matcher = pattern.matcher(url); // 创建匹配器
boolean matches = matcher.find(); // 查找匹配
System.out.println("Matches .com? " + matches);
}
}
性能对比
我们可以通过测量两种方法的执行时间来评估性能。使用 System.currentTimeMillis() 进行简单的时间测量:
java
public class PerformanceTest {
public static void testContains() {
String url = "https://www.example.com";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
url.contains(".com");
}
long endTime = System.currentTimeMillis();
System.out.println("Contains took " + (endTime - startTime) + " ms");
}
public static void testRegex() {
String url = "https://www.example.com";
Pattern pattern = Pattern.compile("\\.com");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
Matcher matcher = pattern.matcher(url);
matcher.find();
}
long endTime = System.currentTimeMillis();
System.out.println("Regex took " + (endTime - startTime) + " ms");
}
public static void main(String[] args) {
testContains();
testRegex();
}
}
运行结果可能会显示 String.contains() 方法快于正则表达式。但在某些情况下,正则表达式可以提供更灵活的匹配方案,例如匹配多个模式。
实验 2:复杂的模式匹配
假设你需要验证一个字符串是否符合某种复杂的模式,比如一个有效的电子邮件地址。你会如何处理?
方案 1:使用多个条件判断
java
public class TestEmail1 {
public static boolean isValidEmail(String email) {
if (email == null || email.isEmpty()) {
return false;
}
if (!email.contains("@")) {
return false;
}
if (email.indexOf('@') == 0 || email.lastIndexOf('@') == email.length() - 1) {
return false;
}
if (!email.contains(".")) {
return false;
}
if (email.indexOf('.') == 0 || email.lastIndexOf('.') == email.length() - 1) {
return false;
}
if (email.lastIndexOf('.') < email.lastIndexOf('@')) {
return false;
}
return true;
}
public static void main(String[] args) {
String email = "test@example.com";
System.out.println("Is valid email? " + isValidEmail(email));
}
}
方案 2:使用正则表达式
java
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class TestEmail2 {
private static final Pattern EMAIL_PATTERN = Pattern.compile(
"^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" // 复杂的正则表达式
);
public static boolean isValidEmail(String email) {
if (email == null) {
return false;
}
Matcher matcher = EMAIL_PATTERN.matcher(email);
return matcher.matches(); // 正则表达式匹配
}
public static void main(String[] args) {
String email = "test@example.com";
System.out.println("Is valid email? " + isValidEmail(email));
}
}
性能对比
同样是通过测量执行时间来评估性能:
java
public class EmailValidationPerformanceTest {
public static void testEmail1() {
String email = "test@example.com";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestEmail1.isValidEmail(email);
}
long endTime = System.currentTimeMillis();
System.out.println("Multiple conditions took " + (endTime - startTime) + " ms");
}
public static void testEmail2() {
String email = "test@example.com";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestEmail2.isValidEmail(email);
}
long endTime = System.currentTimeMillis();
System.out.println("Regex took " + (endTime - startTime) + " ms");
}
public static void main(String[] args) {
testEmail1();
testEmail2();
}
}
运行结果可能会显示正则表达式的性能略逊一筹,但它的代码简洁性和可维护性远超多个条件判断的方法。
实验 3:替换操作
假设你需要将一个字符串中的所有空格替换为下划线。你会如何实现?
方案 1:使用 String.replaceAll() 方法
java
public class TestReplaceAll {
public static void main(String[] args) {
String input = "Hello World This Is Java";
String output = input.replaceAll(" ", "_"); // 使用正则表达式替换空格
System.out.println("Output: " + output);
}
}
方案 2:使用 StringBuilder 手动替换
java
public class TestStringBuilder {
public static String replaceSpaces(String input) {
StringBuilder sb = new StringBuilder();
for (char c : input.toCharArray()) {
if (c == ' ') {
sb.append('_'); // 手动替换空格
} else {
sb.append(c);
}
}
return sb.toString();
}
public static void main(String[] args) {
String input = "Hello World This Is Java";
String output = replaceSpaces(input);
System.out.println("Output: " + output);
}
}
性能对比
再次通过测量执行时间来评估性能:
java
public class ReplacePerformanceTest {
public static void testReplaceAll() {
String input = "Hello World This Is Java";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
input.replaceAll(" ", "_");
}
long endTime = System.currentTimeMillis();
System.out.println("ReplaceAll took " + (endTime - startTime) + " ms");
}
public static void testStringBuilder() {
String input = "Hello World This Is Java";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestStringBuilder.replaceSpaces(input);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder took " + (endTime - startTime) + " ms");
}
public static void main(String[] args) {
testReplaceAll();
testStringBuilder();
}
}
结果可能会显示 StringBuilder 方法更快,但在简单的替换操作中,使用正则表达式可以大大简化代码。
实验 4:分组捕获
假设你需要从一个字符串中提取特定的部分,比如从一个 URL 中提取主机名和路径。你会如何处理?
方案 1:手动解析
java
public class TestManualParsing {
public static String[] extractParts(String url) {
int start = url.indexOf("://") + 3;
int end = url.indexOf('/', start);
String host = url.substring(start, end); // 提取主机名
String path = url.substring(end); // 提取路径
return new String[]{host, path};
}
public static void main(String[] args) {
String url = "https://www.example.com/path/to/resource";
String[] parts = extractParts(url);
System.out.println("Host: " + parts[0]);
System.out.println("Path: " + parts[1]);
}
}
方案 2:使用正则表达式分组捕获
java
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class TestRegexGroup {
private static final Pattern URL_PATTERN = Pattern.compile(
"https?://(.*?)/(.*)" // 使用分组捕获
);
public static String[] extractParts(String url) {
Matcher matcher = URL_PATTERN.matcher(url);
if (matcher.matches()) {
String host = matcher.group(1); // 提取主机名
String path = matcher.group(2); // 提取路径
return new String[]{host, path};
}
return null;
}
public static void main(String[] args) {
String url = "https://www.example.com/path/to/resource";
String[] parts = extractParts(url);
System.out.println("Host: " + parts[0]);
System.out.println("Path: " + parts[1]);
}
}
性能对比
同样的,通过测量执行时间来评估性能:
java
public class GroupCapturePerformanceTest {
public static void testManualParsing() {
String url = "https://www.example.com/path/to/resource";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestManualParsing.extractParts(url);
}
long endTime = System.currentTimeMillis();
System.out.println("Manual parsing took " + (endTime - startTime) + " ms");
}
public static void testRegexGroup() {
String url = "https://www.example.com/path/to/resource";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestRegexGroup.extractParts(url);
}
long endTime = System.currentTimeMillis();
System.out.println("Regex group took " + (endTime - startTime) + " ms");
}
public static void main(String[] args) {
testManualParsing();
testRegexGroup();
}
}
结果可能会显示手动解析更快,但在处理复杂的字符串时,正则表达式的分组捕获功能可以显著减少代码量和复杂性。
实验 5:编译优化
Java 正则表达式的一个重要特性是可以编译正则表达式以提高性能。让我们看看编译前后的性能差异。
方案 1:未编译的正则表达式
java
public class TestUncompiledRegex {
public static void main(String[] args) {
String input = "Hello World This Is Java";
boolean matches = input.matches(".*\\s.*"); // 未编译的正则表达式
System.out.println("Matches? " + matches);
}
}
方案 2:编译后的正则表达式
java
import java.util.regex.Pattern;
public class TestCompiledRegex {
private static final Pattern SPACE_PATTERN = Pattern.compile(".*\\s.*"); // 编译后的正则表达式
public static void main(String[] args) {
String input = "Hello World This Is Java";
boolean matches = SPACE_PATTERN.matcher(input).matches(); // 使用编译后的正则表达式
System.out.println("Matches? " + matches);
}
}
性能对比
通过测量执行时间来评估编译优化的效果:
java
public class CompilationPerformanceTest {
public static void testUncompiled() {
String input = "Hello World This Is Java";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
input.matches(".*\\s.*");
}
long endTime = System.currentTimeMillis();
System.out.println("Uncompiled regex took " + (endTime - startTime) + " ms");
}
public static void testCompiled() {
String input = "Hello World This Is Java";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestCompiledRegex.SPACE_PATTERN.matcher(input).matches();
}
long endTime = System.currentTimeMillis();
System.out.println("Compiled regex took " + (endTime - startTime) + " ms");
}
public static void main(String[] args) {
testUncompiled();
testCompiled();
}
}
结果可能会显示编译后的正则表达式性能明显提升,特别是在多次使用同一正则表达式时。
写在最后
通过上述实验,我们可以看到 Java 正则表达式在不同场景下的表现和优势。虽然在某些简单的情况下,传统的字符串方法可能会更快,但在处理复杂的字符串匹配和替换时,正则表达式的简洁性和灵活性使其成为不可或缺的工具。
如果你对正则表达式的构建和测试有更多需求,不妨试试 Hey Cron。这个网站提供了多种实用的工具,包括正则表达式生成器,可以帮助你快速构建和测试正则表达式,提高开发效率。同时,Hey Cron 还支持 Cron 表达式生成器、中英互译、JSON 格式化、Base64 编码解码、时间戳转换和 JWT 解析等功能,是开发者日常工作中的一大助力。