正则表达式

基础语法

普通字符

包括可见的、直接可以用键盘打出的字符,以及不可见的,或需要用转义字符协助打出的字符(如换行、tab)

直接写在正则表达式中。单独使用时,按照最简单的字面相等进行匹配。

需要注意的是对于类似换行、制表符这样的不可见字符,写在python等程序的代码中时,其实pattern='\t'pattern='\\t'两种写法匹配的效果是一样的。对于第一种,交给正则表达式模块进行处理的时候,拿到的就是制表符本身,当然能匹配制表符;而对于第二种写法,程序语言编译或解释时,将两个\替换为一个真正的\,正则表达式模块处理时拿到的是\t,再按照正则表达式的规则进行一次转义就成了制表符本身,因此也能匹配制表符。这就是为什么Python中下面几对都可以顺利匹配:

s1,p1='''abc
def''','''abc
def'''
print(re.match(p1,s1)) # 匹配

s2,p2='''abc
def''',"abc\ndef"
print(re.match(p2,s2)) # 匹配

s3,p3='''abc
def''',"abc\\ndef"
print(re.match(p3,s3)) # 匹配

特殊字符

正则表达式需要理解语义的字符,用来补充说明匹配的规则。详细说明见下表。

字符 说明
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。要匹配 $ 字符本身,请使用 \$。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 \[。
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。’\n’ 匹配换行符。序列 ‘\‘ 匹配 “",而 ‘(‘ 则匹配 “(“。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 ^。
{ 标记限定符表达式的开始。要匹配 {,请使用 \{。
指明两项之间的一个选择。要匹配 丨,请使用 \丨。

除了转义符之外,下面将他们分为几类详细说明。

通配符 .

小数点(.)做为正则表达式中的通配符,可以匹配除了换行以外的任何一个字符。

限定符

用来限定前面一个字符出现的数量。如果前面有小括号括起来的内容,则规定括号内模式出现的数量。

有以下几个:

  • * 出现0~n次
  • + 出现1~n次
  • ? 出现0~1次
  • {m} 出现m次。{m,}至少出现m次。{,m}至多出现m次。{m,n} 出现m~n次(包含m,n)
print(re.match("(?:abc){2}","abcabc").group(0)) # 匹配
print(re.match("(?:a[bc]d){2}","abdacd").group(0)) # 匹配

贪婪和非贪婪:对于限定符来说,通常只规定了可以匹配多少次,而没有规定必须匹配多少次(除了{m}指定具体此处之外),导致很多时候可以对同一个字符串有多种不同的匹配方式。为了避免歧义,规定限定符默认是贪婪的,即在保持可以匹配成立的情况下尽量多次匹配。如果在限定符后面加上?修饰,表示这里的限定符是非贪婪的,即在保持可以匹配成立的情况下尽量少匹配。这里需要强调的是,一旦有任何一种匹配方法,字符串和正则表达式模式就是可以匹配的,才会考虑贪婪和非贪婪;如果无论如何都找不到一种匹配方法,匹配才会失败,贪婪和非贪婪就无从谈起了。例如:

print(re.match("(?:a[bc]d){,3}(.*)","abdacd").group(1)) # 空 a[bc]d匹配2次
print(re.match("(?:a[bc]d){,3}?(.*)","abdacd").group(1)) # abdacd a[bc]d匹配0次

定位符

用来表示相对匹配字符的相对位置。主要有以下几个:

  • ^ 字符串开头
  • $ 字符串结束
  • \b 单词边界
  • \B 非单词边界

到了正则表达式中出现定位符的位置,必须使得这个定位符出现在描述中的地方。例如如果一个正则表达式以$结尾,就表示必须紧贴字符串末尾进行匹配。

中括号

中括号用来构成中括号表达式。整个中括号表达式可以匹配括号中的任意一个字符。也可以通过-来表示字符范围。常见的有[A-Za-z0-9]表示任意一个字母或数字字符。

小括号

默认的小括号有两个功能:将多个字符或者一个模式视为一个整体,进行限定符修饰;标记字符串中需要提取的有用信息。这种也叫作捕获型括号。

但是有时候只想使用将字符组合打包的功能,而不想提取这些字符,可以用?:进行修饰,处理时就不会对这个括号中的内容进行捕获提取。这种叫做非捕获型括号,把?:称为非捕获元。在上面的例子中两种括号都有出现。

除了?:,还有四个非捕获元?=?<=?!?<!。他们的作用类似定位符,具体说明如下:

  • **exp1(?=exp2)**:查找 exp2 前面的 exp1。
  • (?<=exp2)exp1:查找 exp2 后面的 exp1。
  • **exp1(?!exp2)**:查找后面不是 exp2 的 exp1。
  • (?<!exp2)exp1:查找前面不是 exp2 的 exp1。

或 |

匹配几个字符(或括号)中的一个。因为优先级很低,所以要注意配合括号使用。

print(re.match("((?:abc)|(?:def)|(?:xyz))","xyz").group(1)) # xyz

反向引用

可以用转义符搭配数字代指前面用括号捕获的内容,同时自己这块本身也被提取捕获。比如:

var str = "Is is the cost of of gasoline going up up";
var patt1 = /\b([a-z]+) \1\b/ig; //g表示全局匹配(匹配所有可能匹配的);i表示忽略大小写
document.write(str.match(patt1));
// 作用是可以提取出前后重复的单词 输出 Is is,of of,up up

Python中的使用

引入包

import re

函数

re.match

re.match(pattern, string, flags=0) 从头开始匹配,一次。返回 Match 对象。

import re

pattern = r"123(.*?)abc(1+)234"
text = r"123yyyabc111234---------"

# 从头开始匹配,匹配到第一个就结束
match_obj = re.match(pattern, text)

print(match_obj.group(0)) # 123yyyabc111234 group(0)表示匹配到的整个正则表达式部分
print(match_obj.group(1)) # yyy group(1) 表示匹配到的第一个括号中捕获的内容
print(match_obj.group(2)) # 111 group(2) 表示匹配到的第二个括号中捕获的内容 以此类推

Match.group(num) group(1~n)对应正则表达式中用小括号括起来,捕获的内容。group(0)对应match匹配到和整个正则表达式匹配的内容;按照顺序取出。

flags 标志:

修饰符 含义
re.I 使匹配对大小写不敏感 常用
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使 . 匹配包括换行在内的所有字符。常用
re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

不同的flags可以用 | 来组合。比如re.I | re.S 表示既忽略大小写,.又可以匹配换行。

re.search(pattern,string, flags=0) 搜索,可以从任意位置开始匹配,一次。返回 Match 对象

import re

pattern = r"123(.*?)abc(1+)234"
text = r"2123yyyabc111234---------"

# 从任意位置匹配,查找到第一个就结束
# 如果没有查找到,match_obj为None
match_obj = re.search(pattern, text)

print(match_obj.group(0)) # 123yyyabc111234 group(0)表示匹配到的整个正则表达式部分
print(match_obj.group(1)) # yyy group(1) 表示匹配到的第一个括号中捕获的内容
print(match_obj.group(2)) # 111 group(2) 表示匹配到的第二个括号中捕获的内容 以此类推

findall

re.complie(pattern, flags) flag参数可选。编译为正则表达式 Pattern 对象

Pattern.findall(string) 查找所有匹配到的结果。可以加开始和结束位置参数。

import re

pattern_text = r"123(.*?)abc(1+)234"
text = r"2123YyyABC111234---------"

# 编译后,查找效率会变高 re.I表示忽略大小写
pattern = re.compile(pattern_text, flags=re.I)
result = pattern.findall(text)
print(result)

使用原生字符串

对于代码中的字符串,python会先进行一次转义处理。如果用在正则表达式中,re包对字符串会再进行一次转义处理,相当于进行两次处理。例如输入pattern = "\\n",re拿到时pattern变成"\n",再次转义变成一个换行。本来想匹配一个斜杠和n,却变成了匹配换行。必须使用"\\\\n"才能达到目的。

使用r””原生字符串,可以跳过第一步编译器的处理。例如上面例子中"\\\\n"r"\\n"是等价的。

特殊情况:r"\""被当做一个”来处理。

Java中的使用

API

import java.util.regex.*;

String p="a(.*)";
String s="iiabc";

Pattern pattern = Pattern.compile(p);
Matcher matcher = pattern.matcher(s);

if (matcher.find()) System.out.println(matcher.group(1));

Pattern 类:正则表达式。通过静态compile方法编译得到。

Matcher 类:匹配结果。同样可以像Python一样调用 group(int ) 方法提取捕获的组。

find方法可以在整个字符串中查找正则表达式的第一次匹配,不用必须从开头开始匹配。

//TODO 其他语言的正则表达式使用日后施工