在利用正则表达式的解析过程中,我们经常会遇到多行字符输出的情形,例如执行执行dir命令,如输出如下结果:

1 个文件     59,629,625 字节
7 个目录 41,259,528,192 可用字节

针对这样的输出结果,我们有解析方法:
1. 将整个输出结果作为一个正则表达式解析;
2. 在输出结果中按每行依次进行解析;

方法一:跨行解析

对于跨行解析,首先需要解决的问题是如何匹配换行符,在大部分的应用场景下,可利用“\s”轻松解决问题,但如果要利用“.”匹配换行符,这时候就需要使用DOTALL模式,如下:

//  里面带有换行符
String testTxt = "1 个文件     59,629,625 字节\r\n7 个目录 41,259,528,192 可用字节";
//  (?s)与DOTALL模式相等
Pattern pat = Pattern.compile("(?s)^(\\d+)\\s个文件\\s+([0-9,]+)\\s+字节\\s(.*)$");
//  判断正则表达式是否匹配
if(mat.matches()) {
    System.out.print(mat.group(2));
    System.out.print(mat.group(3));
}

在上面的例子中,我们发现“\r\n”会被当作两个字符,所以必须要使用“\s+”进行匹配,否则将会被划归到“(.*)”分组中。

此外,上面的例子也说明正则表达式可以匹配多行字符,需要注意的是,此时并不需要启用多行模式(“(?m)”或MULTILINE)。

方法二:逐行匹配

在此种模式下,我们必须要启动多行模式,这样^和$字符不仅能匹配输入序列的开始(据我多次测试,好像并不能匹配开始,待进一步证明)与结束,还能匹配每行的开始与结束,如下:

//  请注意起头的\n字符
String testTxt = "\n1 个文件     59,629,625 字节\r\n7 个目录 41,259,528,192 可用字节";
//  (?m)代表启动多行模式
Pattern pat = Pattern.compile("(?m)^(\\d+)\\s个(?:文件|目录)\\s+([0-9,]+)\\s(?:可用)?字节$");
        Matcher mat = pat.matcher(testTxt);
//  提取文件数量与字节数量
while(mat.find()) {
    System.out.println(mat.group(1));
    System.out.println(mat.group(2));
}

在上面的例子中,正则表达式将会寻找输入序列的每行开始与结束,然后依次进行匹配,如果并不需要特别关注每行的开始与结束,可以去掉^、$标识符,依旧能够顺利匹配,如下:

Pattern pat = Pattern.compile("(\\d+)\\s个(?:文件|目录)\\s+([0-9,]+)\\s(?:可用)?字节");

结论

利用多行匹配,可以提升正则表达式的解析效率与适用范围,尤其要关注启用多行模式后的差异性。