正则指引之匹配模式
所谓匹配模式(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