8 Pattern 类的方法

优质
小牛编辑
129浏览
2023-12-01

到目前为止,仅使用测试用具来建立最基本的 Pattern 对象。在这一节中,我们将探讨一些诸如使用标志构建模式、使用内嵌标志表达式等高级的技术。同时也探讨了一些目前还没有讨论过的其他有用的方法。

8.1 使用标志构建模式

Pattern 类定义了备用的 compile 方法,用于接受影响模式匹配方式的标志集。标志参数是一个位掩码,可以是下面公共静态字段中的任意一个:

Pattern.CANON_EQ

启用规范等价。在指定此标志后,当且仅当在其完整的规范分解匹配时,两个字符被视为匹配。例如,表达式a\u030A[8]在指定此标志后,将匹配字符串“\u00E5”(即字符 )。默认情况下,匹配不会采用规范等价。指定此标志可能会对性能会有一定的影响。

Pattern.CASE_INSENSITIVE

启用不区分大小写匹配。默认情况下,仅匹配 US-ASCII 字符集中的字符。Unicode 感知(Unicode-aware)的不区分大小写匹配,可以通过指定 UNICODE_CASE 标志连同此标志来启用。不区分大小写匹配也能通过内嵌标志表达式(?i)来启用。指定此标志可能会对性能会有一定的影响。

Pattern.COMMENTS

模式中允许存在空白和注释。在这种模式下,空白和以#开始的直到行尾的内嵌注释会被忽略。注释模式也能通过内嵌标志表达式(?x)来启用。

Pattern.DOTALL

启用 dotall 模式。在 dotall 模式下,表达式.匹配包括行结束符在内的任意字符。默认情况下,表达式不会匹配行结束符。dotall 模式也通过内嵌标志表达式(?x)来启用。[s 是“单行(single-line)”模式的助记符,与 Perl 中的相同。]

Pattern.LITERAL

启用模式的字面分析。指定该标志后,指定模式的输入字符串作为字面上的字符序列来对待。输入序列中的元字符和转义字符不具有特殊的意义了。
CASE_INSENSITIVE 和 UNICODE_CASE
与此标志一起使用时,会对匹配产生一定的影响。其他的标志就变得多余了。启用字面分析没有内嵌标志表达式。

Pattern.MULTILINE

启用多行(multiline)模式。在多行模式下,表达式^$分别匹配输入序列行结束符前面和行结束符的前面。默认情况下,表达式仅匹配整个输入序列的开始和结尾。多行模式也能通过内嵌标志表达式(?m)来启用。

Pattern.UNICODE_CASE

启用可折叠感知 Unicode(Unicode-aware case folding)大小写。在指定此标志后,需要通过
CASE_INSENSITIVE 标志来启用,不区分大小写区配将在 Unicode 标准的意义上来完成。默认情况下,不区分大小写匹配仅匹配
US-ASCII 字符集中的字符。可折叠感知 Unicode 大小写也能通过内嵌标志表达式(?u)来启用。指定此标志可能会对性能会有一定的影响。

Pattern.UNIX_LINES

启用 Unix 行模式。在这种模式下,.^$的行为仅识别“\n”的行结束符。Unix 行模式可以通过内嵌标志表达式(?d)来启用。

接下来,将修改测试用具 RegexTestHarness.java,用于构建不区分大小写匹配的模式。

首先,修改代码去调用 complie 的另外一个备用的方法:

Pattern pattern = Pattern.compile( 
        console.readLine("%nEnter your regex: "), 
        Pttern.CASE_INSENSITIVE 
    );

编译并运行这个测试用具,会得出下面的结果:

Enter your regex: dog
Enter input string to search: DoGDOg
I found the text "DoG" starting at index 0 and ending at index 3.
I found the text "DOg" starting at index 3 and ending at index 6.

正如你所看到的,不管是否大小写,字符串字面上是“dog”的都产生了匹配。使用多个标志来编译一个模式,使用按位或操作符“|”分隔各个标志。为了更清晰地说明,下面的示例代码使用硬编码(hardcode)的方式,来取代控制台中的读取:

pattern = Pattern.compile("[az]$", Pattern.MULTILINE | Pattern.UNIX_LINES);

也可以使用一个 int 类型的变量来代替:

final int flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; 
Pattern pattern = Pattern.compile("aa", flags);

8.2 内嵌标志表达式

使用内嵌标志表达式(embedded flag expressions)也可以启用不同的标志。对于两个参数的 compile 方法,内嵌标志表达式是可选的,因为它在自身的正则表达式中被指定了。下面的例子使用最初的测试用具(RegexTestHarness.java),使用内嵌标志表达式(?i)来启用不区分大小写的匹配。

Enter your regex: (?i)foo
Enter input string to search: FOOfooFoOfoO
I found the text "FOO" starting at index 0 and ending at index 3.
I found the text "foo" starting at index 3 and ending at index 6.
I found the text "FoO" starting at index 6 and ending at index 9.
I found the text "foO" starting at index 9 and ending at index 12.

所有匹配无关大小写都一次次地成功了。

内嵌标志表达式所对应 Pattern 的公用的访问字段表示如下表:

常量等价的内嵌标志表达式
Pattern.CANON_EQ没有
Pattern.CASE_INSENSITIVE(?i)
Pattern.COMMENTS(?x)
Pattern.MULTILINE(?m)
Pattern.DOTALL(?s)
Pattern.LITERAL没有
Pattern.UNICODE_CASE(?u)
Pattern.UNIX_LINES(?d)

8.3 使用 matches(String, CharSequence) 方法

Pattern 类定义了一个方便的 matches
方法,用于快速地检查模式是否表示给定的输入字符串。与使用所有的公共静态方法一样,应该通过它的类名来调用 matches 方法,诸如
Pattern.matches("\\d","1");。这个例子中,方法返回 true,这是由于数字“1”匹配了正则表达式\d

8.4 使用 split(String) 方法

split 方法是一个重要的工具,用于收集依赖于被匹配的模式任一边的文本。如下面的 SplitDemo.java 所示,split 方法能从“one:two:three:four:five”字符串中解析出“one two three four five”单词:

import java.util.regex.Pattern; 
 
public class SplitDemo { 
 
    private static final String REGEX = ":"; 
    private static final String INPUT = "one:two:three:four:five"; 
     
    public static void main(String[] args) { 
        Pattern p = Pattern.compile(REGEX); 
        String[] items = p.split(INPUT); 
        for(String s : items) { 
            System.out.println(s); 
        } 
    } 
}

输出:

one
two
three
four
five

简而言之,已经使用冒号(:)取代了复杂的正则表达式匹配字符串文字。以后仍会使用 Pattern 和 Matcher 对象,也能使用 split 得到位于任意正则表达式各边的文本。下面的 SplitDemo2.java 是个一样的例子,使用数字作为 split 的参数:

import java.util.regex.Pattern; 
 
public class SplitDemo2 { 
 
    private static final String REGEX = "\\d"; 
    private static final String INPUT = "one9two4three7four1five"; 
 
    public static void main(String[] args) { 
        Pattern p = Pattern.compile(REGEX); 
        String[] items = p.split(INPUT); 
        for(String s : items) { 
            System.out.println(s); 
        } 
    } 
}

输出:

one
two
three
four
five

8.5 其他有用的方法

你可以从下面的方法中找到比较好用的方法:

8.6 在 java.lang.String 中等价的 Pattern 方法

java.lang.String 通过模拟 java.util.regex.Pattern 行为的几个方法,也可以支持正则表达式。方便起见,下面主要摘录了出现在 API 关键的方法。

还有一个替换方法,把一个 CharSequence 替换成另外一个: