正则指引之匹配模式

jopen 10年前

所谓匹配模式(match mode),指的是匹配时使用的规则。设置特定的模式,可能会改变对正则表达式的识别,也可能会改变正则表达式中字符的匹配规定。常用的匹配模式一共有4种:不区分大小写模式单行模式多行模式注释模式

不区分大小写模式

在看这个模式的应用实例之前,必须首先了解模式的指定方式。通常,有两种办法指定匹配模式以模式修饰符指定,或者以预定义的常量作为特殊参数传入来指定。模式修饰符即模式对应的单个字符,使用时将其填入特定结构(?modifier)中(其中的modifier为模式修改符),嵌在正则表达式的开头。比如不区分大小写的匹配模式对应的模式修饰符 i (case Insensitive),对 the 指定此模式,完整的正则表达式就是 (?i)the 。另一种指定模式的方式是使用预定义的常量作为参数,传入正则函数。在Java中,属于Pattern类。下表展示了常用语言中不区分大小写模式的预定义常量:

语言                                              
常量                                                                        
.NET
RegexOptions.IgnoreCase
Java
Pattern.CASE_INSENSITIVE
JavaScript
/regex/i
PHP
/regex/i
Python
re.I
re.IGNORECASE
Ruby
Regexp::IGNORECASE
/regex/i

以Java为例:

//不区分大小写模式  String text1 = "aaa AAA aaaaa AAAA";  Pattern p1 = Pattern.compile("\\b(a{3,5})\\b",Pattern.CASE_INSENSITIVE);  Matcher m1 = p1.matcher(text1);  while(m1.find()){      System.out.println(m1.group(1));  }
//不区分大小写模式(使用模式修饰符)  String text1 = "aaa AAA aaaaa AAAA";  Pattern p1 = Pattern.compile("(?i)\\b(a{3,5})\\b");  Matcher m1 = p1.matcher(text1);  while(m1.find()){      System.out.println(m1.group(1));  }

单行模式

元字符点号(.几乎能匹配任何字符,唯有换行符 \n 是例外。但是,有时候确实需要匹配“任何字符”,比如在处理HTML源代码时,经常会遇到跨越多行的脚本代码:

<script type="text/javascript">  ...code....  ...code....  </script>

正则文档里一般都会说明“点号不能匹配换行符”,不过许多人并不习惯仔细阅读文档,所以认为点号 . 能匹配任何字符,当然也就包括换行符,所以直接的想法是用<script\s.*?</script>来匹配。接下来你就会发现最多只能进行到第一行末尾。之前提到过,可以用[\s\S]之类的字符组匹配“任意字符”,所以正则表达式<script\s[\s\S]*?</script>能解决问题。

不过对大多数人来说,点号更加自然,也更简洁,所以正则表达式提供了单行模式。在这种模式下,所有文本似乎只在一行里,换行符是这一行中的“普通字符”,所以可以由点号 . 匹配。单行模式对应的模式修饰符s (Single line),所以如果用模式修饰符,可以在表达式的开头用(?s)指定:

//单行模式(使用模式修饰符)  String text1 = "<script tyte='text/javascript'>dddddddddd\r\nrrrrrrrrrrrr</script>";  Pattern p1 = Pattern.compile("(?s)<script\\s.*?</script>");  Matcher m1 = p1.matcher(text1);  while(m1.find()){      System.out.println(m1.group());  }
//单行模式(使用叁数)  String text1 = "<script tyte='text/javascript'>dddddddddd\r\nrrrrrrrrrrrr</script>";  Pattern p1 = Pattern.compile("<script\\s.*?</script>",Pattern.DOTALL);  Matcher m1 = p1.matcher(text1);  while(m1.find()){      System.out.println(m1.group());  }

常用语言中单行模式的预定义常量:

语言                                                   
常量                                                                    
.NET
RegexOptions.Singleline
Java
Pattern.DOTALL
JavaScript
不支持此模式
PHP
/regex/s
Python
re.S
re.DOTALL
Ruby
Regexp::MULTILINE
/regex/m

你可能注意到了,单行模式在不同语言中的称呼很不一样。比如在Java和Python中叫 DOTALL(也就是点号通配),这个名字确实更高明。不过,“单行模式”成了约定俗成的称呼。

多行模式

单行模式影响的是点号的匹配规则:在默认模式下,点号 . 可以匹配除换行符之外的任何字符,在单行模式下,点号 . 可以匹配包括换行符在内的任何字符;多行模式影响的是^和$的匹配规则:在默认模式下,^和$匹配的是整个字符串的起始位置和结束位置,但在多行模式下,它们也能匹配字符串内部某一行文本的起始位置和结束位置。

假设,需要找到下面文本中所有数字字符开头的行:

1 line

NO digit

2 line

解决这个问题,需要定位到每行的起始位置,尝试匹配一个数字字符,如果成功,则匹配之后的整行文本。多行模式的模式修饰符 m (Multiline),所以在表达式的开头用(?m)指定多行模式,这样^可以定位到字符串内部每一行的起始位置;匹配数字字符的表达式是 \d ,因为没有指定单行模式,点号 . 不能匹配换行符,.*可以匹配“之后的整行文本”,整个表达式就是 (?m)^\d.* 。示例:

//多行模式(使用修饰符)  String text1 = "1 line\r\nNo digit\r\n2 line";  Pattern p1 = Pattern.compile("(?m)^\\b\\d+\\b");  Matcher m1 = p1.matcher(text1);  while(m1.find()){      System.out.println(m1.group());  }
//多行模式(使用参数)  String text1 = "1 line\r\nNo digit\r\n2 line";  Pattern p1 = Pattern.compile("^\\b\\d+\\b",Pattern.MULTILINE);  Matcher m1 = p1.matcher(text1);  while(m1.find()){      System.out.println(m1.group());  }

下表列出常用语言中预定义常量的写法:

语言                                                     
常量                                                                                
.NET
RegexOptions.Multiline
Java
Pattern.NULTILINE
JavaScript
/regex/m
PHP
/regex/m
Python
re.M
re.MULTILINE
Ruby
默认即为多行模式

注释模式

有时,用到的正则表达式可能非常复杂,不但难以编写和阅读,也难以维护。如果正则表达工也像程序源代码一样,可以添加注释,阅读和维护就容易多了。为了解决这个问题,许多语言支持使用 (?#comment)的记法添加注释,comment就是注释的内容。所以,这样的表达式:^\d.*?$,就可以写成这样:

^(?#start of the line)\d(?#digit).*(?#sest of the line)

.NET,Python,Ruby,PHP都支持这种记法,Java与javascript则不支持

来自:http://my.oschina.net/fhd/blog/370714