Java 正则表达式:比你想象的更强大

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:使用正则表达式 PatternMatcher

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 解析等功能,是开发者日常工作中的一大助力。

相关推荐
yuanyxh4 小时前
macOS 应用 - 纯对话生成
前端·macos·ai编程
大家的林语冰4 小时前
ES5 凉凉,Babel 8 正式发布,默认不再编译为 ES5 和 CJS......
前端·javascript·前端工程化
光影少年6 小时前
react批量更新、同步/异步更新场景
前端·react.js·掘金·金石计划
假如让我当三天老蒯6 小时前
模块化:ES Module 与 CommonJS 的区别
前端·面试
用户40950115773176 小时前
Private Forge v2.0 发布:12大前端业务场景技能系统
前端
weedsfly6 小时前
异步编程全景与事件循环——彻底搞懂 JS 执行机制
前端·javascript
用户059540174466 小时前
AI Agent记忆测试踩坑实录:Mock骗了我一周,Mem0+pytest一招破局
前端·css
用户1733598075376 小时前
纯前端 PDF 数字签名实战:Vue 3 + pdf-lib 在浏览器里完成签名嵌入
前端·javascript
IT_陈寒7 小时前
SpringBoot自动配置的坑,我爬了三天才出来
前端·人工智能·后端