ZetCode

Java Pattern.quote 方法

上次修改时间:2025 年 4 月 20 日

Pattern.quote 方法是 Java 正则表达式包中的一个静态工具方法,它返回一个字面量模式字符串。 它转义了输入字符串中的所有特殊正则表达式元字符。 这使得字符串在正则表达式中进行精确匹配时是安全的。

当您需要按字面量匹配可能包含正则表达式特殊字符的字符串时,Pattern.quote 是必不可少的。 它将字符串包裹在 \Q\E 标记中,将它们之间的所有内容视为字面文本。

基本定义

Pattern.quote(String s) 接受一个 String 参数并返回一个被引用的 String。 即使返回的字符串包含正则表达式元字符,如 *+?,它也将与输入字符串完全匹配。

此方法在从用户输入或文件路径动态构建模式时特别有用。 如果没有引用,此类字符串可能导致正则表达式语法错误或意外的匹配行为。

基本用法

此示例演示了 Pattern.quote 的基本用法,以按字面量匹配包含正则表达式元字符的字符串。

PatternQuoteBasic.java
package com.zetcode;

import java.util.regex.Pattern;

public class PatternQuoteBasic {

    public static void main(String[] args) {
        String input = "The price is $10.99 (special offer)";
        String search = "$10.99 (special offer)";
        
        // Without quoting - fails because $ is a regex metacharacter
        boolean withoutQuote = Pattern.matches(".*" + search + ".*", input);
        System.out.println("Without quote: " + withoutQuote);
        
        // With quoting - works correctly
        String quoted = Pattern.quote(search);
        boolean withQuote = Pattern.matches(".*" + quoted + ".*", input);
        System.out.println("With quote: " + withQuote);
    }
}

第一次尝试失败,因为 $ 是一个正则表达式元字符,它锚定在行尾。 引用后,所有字符都被视为字面量,匹配成功。 输出显示为 false 然后 true。

匹配文件路径

文件路径通常包含反斜杠和其他需要在正则表达式模式中转义的特殊字符。 Pattern.quote 会自动处理此问题。

PatternQuoteFilePath.java
package com.zetcode;

import java.util.regex.Pattern;

public class PatternQuoteFilePath {

    public static void main(String[] args) {
        String logEntry = "File saved to C:\\Users\\Documents\\report.pdf";
        String filePath = "C:\\Users\\Documents\\report.pdf";
        
        // Without quoting - fails due to unescaped backslashes
        try {
            boolean match = Pattern.matches(".*" + filePath + ".*", logEntry);
            System.out.println("Without quote: " + match);
        } catch (Exception e) {
            System.out.println("Error without quote: " + e.getMessage());
        }
        
        // With quoting - works correctly
        String quotedPath = Pattern.quote(filePath);
        boolean match = Pattern.matches(".*" + quotedPath + ".*", logEntry);
        System.out.println("With quote: " + match);
    }
}

未引用的版本会抛出一个异常,因为反斜杠必须在正则表达式模式中转义。 Pattern.quote 正确地转义了所有特殊字符,使匹配按预期工作。

动态模式构建

从变量构建模式时,Pattern.quote 确保用户提供的字符串被视为字面量,从而防止正则表达式注入。

PatternQuoteDynamic.java
package com.zetcode;

import java.util.regex.Pattern;

public class PatternQuoteDynamic {

    public static void main(String[] args) {
        String userInput = "[Important] Meeting at 3pm";
        String message = "Reminder: [Important] Meeting at 3pm - Don't forget!";
        
        // Dangerous way - userInput could contain regex syntax
        boolean unsafeMatch = Pattern.matches(".*" + userInput + ".*", message);
        System.out.println("Unsafe match: " + unsafeMatch);
        
        // Safe way with Pattern.quote
        String safePattern = ".*" + Pattern.quote(userInput) + ".*";
        boolean safeMatch = Pattern.matches(safePattern, message);
        System.out.println("Safe match: " + safeMatch);
        
        // Test with malicious input
        String maliciousInput = ".*";
        String testMessage = "This should not match everything";
        
        // Without quote - matches everything!
        boolean badMatch = Pattern.matches(".*" + maliciousInput + ".*", testMessage);
        System.out.println("Malicious without quote: " + badMatch);
        
        // With quote - matches literally
        boolean goodMatch = Pattern.matches(".*" + Pattern.quote(maliciousInput) + ".*", testMessage);
        System.out.println("Malicious with quote: " + goodMatch);
    }
}

此示例显示了未引用的用户输入如何导致安全漏洞。 恶意输入 ".*" 将匹配任何字符串,而无需引用。 Pattern.quote 通过将输入视为字面量来防止这种情况。

使用字面量分隔符拆分

当使用复杂的分隔符拆分字符串时,Pattern.quote 确保分隔符被视为字面量,而不是正则表达式模式。

PatternQuoteSplit.java
package com.zetcode;

import java.util.regex.Pattern;

public class PatternQuoteSplit {

    public static void main(String[] args) {
        String data = "apple|orange|banana|grape";
        String delimiter = "|"; // Pipe is a regex metacharacter
        
        // Without quoting - splits every character
        String[] badSplit = data.split(delimiter);
        System.out.println("Without quote split count: " + badSplit.length);
        
        // With quoting - splits correctly at pipes
        String[] goodSplit = data.split(Pattern.quote(delimiter));
        System.out.println("With quote split count: " + goodSplit.length);
        
        // Print the results
        System.out.println("\nGood split results:");
        for (String fruit : goodSplit) {
            System.out.println(fruit);
        }
    }
}

管道字符 | 是一个正则表达式交替运算符。 如果没有引用,split 会将其视为一个模式,并在每个字符处拆分。 引用后,它仅在字面量管道字符处拆分。

转义替换字符串

虽然主要用于模式,但 Pattern.quote 也可以在 Matcher.replaceAll 中处理替换字符串时提供帮助。

PatternQuoteReplacement.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PatternQuoteReplacement {

    public static void main(String[] args) {
        String text = "Replace $10 with $20 in the budget";
        String find = "$10";
        String replace = "$20";
        
        // Without quoting - $ is treated as group reference
        try {
            String badResult = text.replaceAll(find, replace);
            System.out.println("Without quote: " + badResult);
        } catch (Exception e) {
            System.out.println("Error without quote: " + e.getMessage());
        }
        
        // With quoting - works correctly
        String goodResult = text.replaceAll(Pattern.quote(find), 
            Matcher.quoteReplacement(replace));
        System.out.println("With quote: " + goodResult);
    }
}

此示例显示 Pattern.quote 处理搜索模式,而 Matcher.quoteReplacement 需要用于替换字符串。 它们一起确保两部分都被视为字面量。

与其他模式结合使用

Pattern.quote 可以与其他正则表达式模式结合使用,以创建复杂的匹配逻辑,同时保持某些部分为字面量。

PatternQuoteCombined.java
package com.zetcode;

import java.util.regex.Pattern;

public class PatternQuoteCombined {

    public static void main(String[] args) {
        String[] filenames = {
            "report_2023.pdf",
            "report_2023.txt",
            "invoice_2023.pdf",
            "summary_2023.pdf"
        };
        
        String fixedPart = "report_2023";
        String extension = ".pdf";
        
        // Build pattern combining quoted and regex parts
        String pattern = Pattern.quote(fixedPart) + "\\..+";
        Pattern compiled = Pattern.compile(pattern);
        
        System.out.println("Matching files:");
        for (String filename : filenames) {
            if (compiled.matcher(filename).matches()) {
                System.out.println(filename);
            }
        }
        
        // More precise version requiring specific extension
        String precisePattern = Pattern.quote(fixedPart) + 
            Pattern.quote(extension);
        System.out.println("\nExact matches:");
        for (String filename : filenames) {
            if (filename.matches(precisePattern)) {
                System.out.println(filename);
            }
        }
    }
}

此示例显示了如何将引用的字面量字符串与正则表达式模式相结合。 第一个模式匹配以 "report_2023" 开头并具有任何扩展名的任何文件。 第二个模式完全匹配 "report_2023.pdf"。

性能注意事项

虽然 Pattern.quote 会增加一些开销,但与未引用的模式导致的错误匹配或异常的成本相比,通常可以忽略不计。

PatternQuotePerformance.java
package com.zetcode;

import java.util.regex.Pattern;

public class PatternQuotePerformance {

    public static void main(String[] args) {
        String input = "Searching in 1,000,000 strings for special chars: *^$";
        String search = "*^$";
        
        // Test without quoting
        long start = System.nanoTime();
        try {
            boolean match = input.matches(".*" + search + ".*");
        } catch (Exception e) {
            System.out.println("Exception without quoting");
        }
        long withoutQuoteTime = System.nanoTime() - start;
        
        // Test with quoting
        start = System.nanoTime();
        boolean match = input.matches(".*" + Pattern.quote(search) + ".*");
        long withQuoteTime = System.nanoTime() - start;
        
        System.out.println("Time without quote: " + withoutQuoteTime + " ns");
        System.out.println("Time with quote: " + withQuoteTime + " ns");
        System.out.println("Overhead: " + 
            (withQuoteTime - withoutQuoteTime) + " ns");
    }
}

此示例比较了引用模式与未引用模式的性能。 虽然引用会增加少量开销,但对于大多数应用程序而言,这通常可以忽略不计。 安全性优势通常大于最小的性能成本。

来源

Java Pattern.quote 文档

Pattern.quote 方法是 Java 中安全正则表达式操作的必备工具。 它可防止正则表达式注入漏洞,并确保包含特殊字符的字符串的字面量匹配。

作者

我的名字是 Jan Bodnar,是一位经验丰富的程序员,在该领域拥有多年的经验。 我于 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和 8 本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程