什么是正则表达式
正则表达式,英文名Regular Expression,常见缩写regex、regexp或RE都指代正则表达式。
正则表达式是对句法规则的一种描述。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
正则表达式是由一些基本的集合及对集合的运算组成的。
基本元素
普通字符 字母、数字、汉字、下划线、以及后边章节中没有特殊定义的标点符号,都是"普通字符"。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
举例:
表达式 "c",在匹配字符串 "abcde" 时,匹配结果是:成功;匹配到的内容是:"c",匹配到的位置是:开始于2,结束于3。(注:下标从0开始还是从1开始,因当前编程语言的不同而可能不同)
基本运算
交集
如果有两个正则表达式E和F,那么EF也是一个正则,表示同时匹配E和F的内容。这跟编程中的逻辑与是一个意思,跟集合中的交集也是一个意思。你也可以连接任意多个正则。
举例:
表达式 "bc",在匹配字符串 "abcde" 时,匹配结果是:成功;匹配到的内容是:"bc",匹配到的位置是:开始于1,结束于3。
并集
如果有两个正则表达式E和F,那么E|F也是一个正则,表示匹配E或者匹配F。这跟编程中的逻辑或是一个意思,跟集合中的并集也是一个意思。 你可以使用|连接任意多个正则表达式。
如果你要连接非常多的正则,那就得写非常多的竖线,看起来非常乱。所以人们还约定了一种简化记法[EFGH],跟写成E|F|G|H效果是一样,但前者更简短,更清晰。
举例:
表达式 " [bcd][bcd] " 匹配 "abc123" 时,匹配的结果是:成功;匹配到的内容是:"bc";匹配到的位置是:开始于1,结束于3。
补集
如果你要排除匹配E和F的内容,需要写成 [ ^EF ]
举例:
表达式 "[^abc]" 匹配 "abc123" 时,匹配的结果是:成功;匹配到的内容是:"1";匹配到的位置是:开始于3,结束于4。
单元素集合
连字符表示多个元素
一个字母a, 一个数字1都是正则,分别匹配包含a和包含1的内容。
如果我们想匹配数字1234,那么根据交集规则,我们写成1234就可以了。
如果我们想匹配所有可能出现的数字,则可以根据并集规则写成0|1|2|3|4|5|6|7|8|9。是不有点长。我们可以简化成[0123456789]。一下子少了很多坚线。
慢着,如果想匹配所有可能出现的小写字母呢?难不成要将26个字母全部写入正则表达式中?
正则表达式为此提供了一种更加简化方法——连字符,可以使用减号-表示连续出现的字符,只需写出头尾。所以我们可以把[0123456789]进一步简化成[0-9],把[abc...xyz]简化成[a-z]。
\d
因为[0-9]很常用,大家又进一步简化成了\d(对应单词 digit)。
\a
因为[a-zA-Z]也很常用,大家就把它简化成\a(对应单词 alpha)。
\w
果想匹配大小写字母、数字和下划线(也就是所有单词字符),可以写成[a-zA-Z0-9_]。也是因为太常用,大家将其简化为\w(对应单词 word)。
\s
如果想匹配一些空白字符,可以写成[ \t\r\n\v\f],这个正则会匹配空格、水平制表符、回车、换行、 垂直制表符和 Page break 记。这里用到了跟 c 语言 printf 函数一样的转义字符。同样因为使用广泛,被简化为\s(对应单词 space)。
取反——大写
正则支持取反操作。[0-9]表示匹配所有数字,那[ ^0-9 ]就表示匹配所有非数字字符。因为使用广泛,人们将其简写成\D。大家注意,[0-9]简写成\d(小写字母),对应的[ ^0-9 ]简写成\D(大写字母)。以此类推,\a取反是\A、\w取反是\W、\s取反是\S。
全集
有了连字符和并集规则,理论上我们可以匹配所有字符。但是 Unicode 有上百万字符,难道我们都要写到方括号里吗?不能够。
我们可以利用取反操作。只要排除少量不常用字符,就可以匹配剩下的大多数字符了。但排除哪个呢?最终人们决定排除\n。为什么呢?因为一般而言,正则都是逐行匹配的,一次匹配一行内容,不会遇到换行符。最终可以用[ ^\n ]表示匹配所有字符。同样因为太常用,这一写法被简化成句点'.'。 也就是说在正则表达式中,一个.可以匹配\n以外的所有字符。
转义字符\
多元素集合
大括号表示一次匹配多个元素
如果想匹配两位数字,可以利用交集规则,写成\d\d,此正则会先匹配一个数字再匹配一个数字,最终匹配的是两位数字。如果想匹配三位数字,需要写成\d\d\d,四位数字写成\d\d\d\d。
那如果想匹配一位或者两位数字或者三位数字或者四位数字(也就是四位以内的数字),需要写成
\d|\d\d|\d\d\d|\d\d\d\d
有点长,但是 it works!如果想匹配所有的八位以内的数字呢?那就得写很长很长了。为此,人们又想了个简化的办法。这次引入了大括号{}。
刚才的正则是可以简化成\d{1,4},展开就是\d|\d\d|\d\d\d|\d\d\d\d。大括号中第一个数字表示最短匹配的次数,第二个数字表示最长匹配的次数。
如果想匹配八位以内的数字,就可以写成\d{1,8},是不是很简洁呢?
如果只想匹配一个八位数,则可以写成\d{8,8}。重复写两个8好像有点多余,还是简化成\d{8}吧。
+号
那能不能匹配任意长度的数字呢? 理论上应该写成\d{1,∞},只是这个∞不好写,干脆省略,写成\d{1,}算了。所以\d{1,}表示可以匹配一位、两位、一直到任意长度的数字。也就是说{1,}表示前面的匹配内容至少出现一次。因为这个至少出现一次也是特别常用,人们又进一步将其简化成+,最终我们的正则变成了\d+,优雅的不行。
*号
那能不能实现匹配出现零次这种语义呢? 可以,只要将大括号内第一个数字写成0就行。所以a{0,}可以匹配a、aa……aaaa……等各种情况。也就是说{0,}表示前面的匹配的内容出现多次或者不出现。 同样十分常用,被人们简化成了。所以原来的正则可以简化成a。
?号
最后就是{0,1}这种情况了,显然表示出现零次或者一次。 不用说,懒人们将其简化成了?。所以ab?只能匹配a和ab两种情况。
一些常见的用法
贪婪与非贪婪模式
正则表达式默认使用贪心模式,加入?改为非贪婪模式
常见的用法有
.*为贪婪匹配,从最长字符串开始匹配,可回溯
.*?为非贪婪匹配,具有最小匹配性质
例如:
<img src="test.jpg" width="60px" height="80px"/>
用表达式src=".*"匹配可以得到src="test.jpg" width="60px" height="80px"
匹配时会一直检索到最后一个引号再停止
表达式src=".*?"可以得到结果:src="test.jpg"
匹配到第一个"就结束了一次匹配。不会继续向后匹配。
引用——使用前面括号内的内容
环视
o.o 看不太懂
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。