An Introduction to Regular Expression

正则表达式必知必会

Posted by Cliu on December 26, 2023

初识正则

正则表达式是一些用来匹配(搜索)和处理(替换)文本的字符串。

正则表达式是文本处理的强大工具,作用对象是文本/字符串。

具体到某一个正则表达式,也称为模式(Pattern),模式由字面量(可由十六进制表示\x开头或八进制表示\0开头),元字符(. \ [等)或两者组合而成。

有特殊含义的字符要加反斜杠 \ 转义。\ 本身也是一个元字符(不表示其本身含义而具有特殊含义的字符)。

元字符有两类:一类用于匹配文本(.*+等),另一类是正则表达式语法所要求的,如\ [等。

用法大全

匹配任意字符

. 英文句号,可以匹配除换行符以外的任意单个字符

匹配一组字符

[] 定义一个字符集合/字符类,匹配的结果是与任意一个集合成员相匹配的字符

  1. 常用于局部不需要区分大小写的情形,如[Cc]hang
  2. 字符集合中的有些元字符不需要转义(如.+),当然转义也没有错:[\w.][\w\.]等效
  3. 字符集合中可以使用字符区间如0-9,a-z,A-Z,其中-(连字符)是一个定义字符区间的元字符。-并不需要转义,它仅在连接字符区间端点的时候有特殊含义,其他时候都是一个字面量,如[0-9-]
  4. 字符区间的端点可以是ASCII字符表里的任意字符,但实际中最常用数字字符区间和字母字符区间
  5. A和a的ASCII码分别是65和97,而97-65=32(Z和a之间还有[\等6个符号),所以一般不用[A-z]这一字符区间。

取非匹配

^ 用于 [ 和 ] 之间并且紧跟在 [ 后面,如[^abc]表示除a,b,c之外的任意单个字符

字符集合的嵌套和运算

[A-Za-z0-9], [a-z&&[^bc]],其中&&为与操作符,另|为或操作符(注意优先级)

特殊的字符类

1
2
3
4
5
6
7
8
9
10
11
\d<==>[0-9] 任何一个数字字符
\D<==>[^0-9] 任何一个非数字字符
\w<==>[0-9A-Z_a-z] 任何一个单词字符(大小写字母、数字、下划线)
\W<==>[^0-9A-Z_a-z]
\s<==>[\f\n\r\t\v] 任何一个空白字符(不包括退格符\b)
\S<==>[^\f\n\r\t\v]
POSIX字符类:
[[:lower:]]<==>[a-z] 匹配小写
[[:upper:]]<==>[A-Z] 匹配大写字母
[[:alpha:]]<==>[a-zA-Z] 匹配大小写字母
字符类\d,\w,\s等也是一种元字符,这里\其后的字母整体构成元字符

重复匹配

它们都是贪婪型的元字符,可能会导致过渡匹配,它们的懒惰型版本只用在其后多加一个问号?

如对于字符串”acbacb”,模式a.*b匹配整个字符串,但a.*?b仅与”acb”匹配

1
2
3
4
5
6
7
.
*
+
{ 重复次数 }
{3}
{0,3}
{3, }   

位置匹配

使用边界限定符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
\b 单词边界,匹配\w和\W之间的位置
如"I |am Cliu",所处的位置位于空格和a之间,与\b匹配
\b匹配单词的开头、结尾,要匹配整个单词,要在该单词前后都加上\b

\B 非单词边界。
如\B-\B匹配case - sensitive中前后有空格的连字符,因为空格和-都不和\w匹配

^ $ 字符串边界
^和$匹配字符串开头、结尾两个位置,而不是字符,相比于\b常用于字符串内/行内单词匹配,^和$常用来匹配一整行/一整个字符串

(?m)字符串分行匹配模式标志
置于整个模式的开头,可避免将一个含有多行的文件当作一个字符串匹配

(?i)模式不区分大小写标志
这两个标志的功能都可以通过Pattern中的常量(flags)来实现且性能可能更好:
对应MIULTILINE和CASE_INSENSITIVE

子表达式

( )内的多个字符/表达式被当作一个整体来对待

可以用于控制优先级,设置一组字符的重复以及回溯引用(\1,\2 …相当于变量,其值为模式的第1,2个子表达式)

m|food匹配”m”或”food”,因为字符优先级高于|运算符,(m|f)ood才匹配”mood”或”food”

回溯引用指在模式的后半部分引用前半部分的定义的子表达式,用作模式内部引用,文本替换/重排等,如(pattern)\1表示pattern连续出现两次

替换

回溯引用在查找和替换中分别写作\1$1

1
2
3
4
5
\E    结束\L或\U转换
\l    把下一个字符或子表达式转换为小写
\L    把\L后面的所有字符转换为小写,除非遇到\E
\u    把下一个字符或子表达式转换为大写
\U    把\U后面的所有字符转换为大写,除非遇到\E

前后查找

1
2
3
4
(?=regex) 向前查找
(?<=regex) 向后查找,regex必须是定长的,即不能包含* + {3, }等变长参数
(?!regex) 负向前查找(向前查找取非)
(?<!regex) 负向后查找(向后查找取非)

向前查找和向后查找,首先是一个子表达式,指定那些要求匹配但不在结果中返回的模式(不捕获)

实质上向前查找指定了它前面的文本必须以regex结尾,向后查找指定了它后面的文本必须以regex开头,即匹配结果处于什么位置

在正则表达式中嵌入条件

1
(?(condition)X|Y) // 根据condition是否匹配到执行后续模式X或Y的匹配,|Y可省略

其中,(condition)可以是表示回溯引用的数字(1),(2),(3)...,也可以是表示前后查找匹配的(?=pattern)

应用

MySQl中的正则表达式

1
2
--MySQL的where子句对正则表达式提供了支持
select * from db where Sname REGEXP "liu?(先生|女士)"

MySQl中正则表达式搜索默认不区分大小写,如果要区分大小写,用 …Sname REGEXP BINARY “liu?(先生|女士)”

LIKE匹配整个列,而REGEXP只要有该子串匹配就返回该列,也可以使用^,$让正则表达式匹配整个列

MySQl使用两个反斜杠转义,如\n应转义为\n.,\应转义为\\,MySQL本身解释一个,正则表达式库解释另一个

可以使用类似select “Hello” REGEXP “.+”这样的语句测试正则表达式 字符类应采用POSIX字符类,如[[:lower:]]等

Java中的正则表达式

见java.util.regex.Pattern类:

典型用法

1
2
3
4
5
6
7
8
9
10
11
 //将正则表达式编译为Pattern类实例,可重用,效率高
 Pattern p = Pattern.compile("a*b",flags);

 //由模式p创建Matcher类对象并与字符序列(参数)进行匹配
 Matcher m = p.matcher("aaaaab");

 //匹配结果都驻留在匹配器m中,可以执行三种匹配操作matches()、find()或lookingAt()
 boolean b = m.matches();

 //结果为true
 Matcher m = Pattern.compile(“\\\\”).matcher(“\\”);

\\n和\n将被编译为相同的模式,因为java编译器和正则表达式解析器都认识\n(同样,它们还共同认识\u2014等unicode字符)。 Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义(valid escape sequences are \b \t \n \f \r \” \’ \\)。

于是Java编译器将”\n”中的第一个\连同其后的\理解成代表反斜杠的转义字符,于是传给正则表达式解析器的就是”\n”。 另一方面,Java编译器将”\n”解释成换行符传给正则表达式解析器,它当然也认识。

Kotlin中的正则表达式

见kotlin.text.Regex类

Kotlin中的三重引号字符串”"”pattern””“,指定的模式无需转义

常见问题的正则表达式解决方案

  1. IP地址
    由4个字节组成,每个字节取值范围都是0~255,通常被写为4组以.分隔的整数(1~3位)
    1
    2
    3
    4
    
    //粗匹配
    (\d{1,3}\.){3}\d{1,3}
    //准确匹配
    (((\d{1,2}|(1\d{2})|(2[0-4]\d)|(25[0-5])))\.){3}((\d{1,2}|(1\d{2})|(2[0-4]\d)|(25[0-5])))
    
  2. URL地址
    1
    
    https?://[-\w.]+(:\d+)?(/([\w/_.]*)?)?
    
  3. 电子邮件地址
    1
    
    (\w+\.)*\w+@(\w+\.)+[[:alpha:]]+