文:郑元春
人生苦短,我用Python!
0x01:什么是正则表达式
In theoretical computer science and formal language theory, a regular expression (sometimes called a rational expression)[1][2] is a sequence of characters that define a search pattern, mainly for use in pattern matching with strings, or string matching, i.e. "find and replace"-like operations. The concept arose in the 1950s, when the American mathematician Stephen Kleene formalized the description of a regular language, and came into common use with the Unix text processing utilities ed, an editor, and grep, a filter.
--《Wiki Regular expression》
中文简述:在理论计算科学领域和形式化语言理论中,regular expression(有时候也叫做rational expression)是一种定义了搜索模式的特征序列,主要是用于字符串的模式匹配,或是字符的匹配。
熟悉Linux的同学肯定在shell中就用过各种各样的正则表达式,基本上每种语言,像是C++,Java,JavaScript和Python等都是支持正则表达式的应用。有的时候书写起来可能会有细微的差别,并且对正则符号的支持程度也不一样。
由于近来要做些NLP之类的处理,所以就学习了下Python中的正则表达式。学会使用正则表达式真的会极大的提高你的开发效率,但是正则表达式学习起来是有一定的门槛的,因为里面会牵扯到一定的模式抽象能力,相比于其他的问题来说有点难度。不过,作为一个程序员,被人称为情商加到智商上的生物,我们不应该就是勇往直前么。
看过一些大牛写的正则表达式,真的是既简洁又很优雅。并且很神奇的能够处理那些看似复杂的文本解析任务。我的个人理解是正则表达式就是一组高效的规则的模式集合
。
某个程序员的理解是:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
0x02:Python中的正则表达式
正则表达式并不是Python的一部分,效率上可能不如str自带的方法,但功能十分强大。
1.字符类
- 1.1一般字符(就是简单的字符)
就是没有任何正则元字符的正常字符串
比如匹配abcababc
将会找到两个 abc
,需要注意的是匹配的时候是从前向后挨个匹配的,像 ababa
只能找到一个aba
- 1.2 \
将正则元字符(当成正则式中的关键字)转义成正常的字符。
import re #记得载入正则模块
#case 1 没有使用转义
reStr='a.c'
targetStr="abc a.c a\\c"
re.findall(reStr,targetStr)
#结果是:
>>>['abc', 'a.c', 'a\\\\c']
解释:因为你这里没有使用转义,所以a.c
中的.
就是一个正则元字符(.
的作用会在下面讲解),所以上面的代码会选出多个符合条件的子字符串。
#case 2 使用转义
reStr='a\\.c'
re.findall(reStr,targetStr)
#结果是:
>>>['a.c',]
```
*解释*:这样就只把`a.c`给提取出来了。
- 1.3`.`
代替任何单个字符(换行除外)
```
reStr="a.c"
targetStr="abc a.c abb"
re.findall(reStr,targetStr)
>>>['abc','a.c']
```
- 1.4`[]`
对应的位置可以是字符集的任何一个字符。除了直接给出字符集,你还可以给出字符范围
```
#case 1:直接列出字符集
reStr="a[12bc]ef"
targetStr="a1ef abef a12bcef"
re.findall(reStr,targetStr)
>>>['a1ef', 'abef']
#case 2:使用范围
reStr="a[1-5a-d]ef"
targetStr="a3ef a6ef acef a1eef"
re.findall(reStr,targetStr)
>>>['a3ef', 'acef']
#当然[]中你还可以添加其他的规则
```
#### 2.预定义字符类(可以写在[]中的)
- 2.1`\\d`和`\\D`
数字:`\\d`=`[0-9]`
非数字:`\\D`=`[^\\d]`=`[^0-9]`
```
targetStr="123abc"
#case 1:数字
reStr="\\d"
re.findall(reStr,targetStr)
>>>['1','2','3']
#case 2:非数字
reStr="\\D"
re.findall(reStr,targetStr)
>>>['a','b','c']
#case 3:非数字
reStr="[^0-9]"
re.findall(reStr,targetStr)
>>>['a','b','c']
```
*注意:不推荐使用`[^\\d]` 或者是`[^\\D]`的表示方式,如果你忘记了[],结果就会是错误的*
- 2.2`\\s`和`\\S`
空白字符:`\\s`=[<space>\\t\\r\\n\\f\\v]
非空白字符:`\\S`=[^\\s]
```
#case 1:空白字符
reStr="\\s"
targetStr="a\\nb a\\tb"
re.findall(reStr,targetStr)
>>>['\\n', ' ', '\\t']
#case 2:非空白字符
reStr="a\\Sc"
targetStr="abc a.c"
re.findall(reStr,targetStr)
>>>['abc', 'a.c']
```
空白字符是包括所有的空格字符,有的文本编辑器会使用<space>替代`tab` 键,此时需要注意。`\\S`和`.`的区别是`.`包括了`\\S`.
- 2.3`\\w`和`\\W`
单词字符:`\\w`=[A-Za-z0-9_]
非单词字符:`\\W`=[^\\w]
```
#case 1:能够组成单词的字符
reStr="a\\wc"
targetStr="abca2caBca c"
re.findall(reStr,targetStr)
>>>['abc', 'a2c', 'aBc']
```
#### 3.数量词(用字字符或是`()`之后)
- 3.1`*`
匹配请一个字符0次或者是无数次
```
reStr="a*c" #匹配 'c' 'ac' 'aac' 'a...c'
targetStr="ac aac abc aaac"
re.findall(reStr,targetStr)
>>>['ac', 'aac', 'c', 'aaac']
```
*注意abc中c是符合的*
- 3.2`+`
匹配前一个字符1次或者是无数次
```
reStr="a+c" #匹配 'ac' 'aac' 'a...c'
targetStr="ac aac abc aaac"
re.findall(reStr,targetStr)
>>>['ac', 'aac', 'aaac']
```
- 3.3`?`
匹配前一个字符0次或者是1次
```
reStr="a?c" #匹配 'c' 'ac'
targetStr="ac aac abc aaac"
re.findall(reStr,targetStr)
>>>['ac', 'ac', 'c','ac']
```
- 3.4`{m}`
匹配前一个字符m次
```
reStr="a{2}c" #只匹配 "aac"
targetStr="ac aac abc aaac"
re.findall(reStr,targetStr)
>>>['aac','aac']
```
- 3.5`{m,n}`
匹配前一个字符从m次到n次
当只有m的时候表示m-无数次
当只有n的时候表示0-n次
```
reStr="a{1,2}c" #只匹配 'ac' 'aac'
targetStr="ac aac abc aaac"
re.findall(reStr,targetStr)
>>>['ac', 'aac', 'aac']
```
#### 4.边界匹配
- 4.1`^`
匹配字符串开头
在多行模式中匹配每一行的开头
```
#case 1:没有开启多行模式
reStr="^abc"
targetStr="abc\\nabcd\\nbabc"
re.findall(reStr,targetStr)
>>>['abc']
#case 2:开启多行模式
r=re.compile(reStr,re.M)
r.findall(targetStr)
>>>['abc', 'abc']
```
- 4.2`$`
匹配字符串末尾
在多行模式中匹配每一行的末尾
```
reStr="-a$"
targetStr="abc-a\\n123-a\\n-a\\na\\n-b"
r=re.compile(reStr,re.M)
r.findall(targetStr)
>>>['-a', '-a', '-a']
```
- 4.3`\\A`
仅匹配字符串开头
```
reStr="\\Aabc"
targetStr="abcabc"
re.findall(reStr,targetStr)
>>>['abc']
```
这个只会匹配字符串的开头,只要是开头没有的话那就是没匹配上
- 4.4`\\Z`
仅匹配字符串结尾
```
reStr="abc\\Z"
targetStr="aaabc"
re.findall(reStr,targetStr)
>>['abd']
```
- 4.5`\\b`
匹配那些在单词字符和非单词字符之间的“位置”(之所以称这个为位置,是因为这不是一个字符,比如`a-`中是在`a`和`-`之间的位置),姑且认为他是一个特殊字符吧!
>注意在Python中`\\b`代表着是是退格键的意思,如果要使用正则表达式的话需要转义以下,就是使用`\\\\b`,当然,你也可以使用`r'\\b'`使用Python的raw型的字符串(注意前面的`r`).其实就是内部自动做了转义。
```
#case 1: 单纯的`\\\\b`匹配问题
reStr="\\\\b"
targetStr="a a a"
re.findall(reStr,targetStr)
>>>['', '', '', '', '', '']
```
>为什么这里会是6个“位置”呢?注意的实际情况是这样的:中间有两个非单词字符(空格),每个空格跟左右的单词字符有4个“位置”,再加上整个字符串的首位2个位置,所有有6个位置。是不是更加的深入了解了“位置”的意思了呢。
```
#case 2:字符和"\\\\b"的搭配使用
targetStr="ao-ao-ao"
reStr=r"ao\\b" #注意这里的`r`
re.findall(reStr,targetStr)
>>>['ao','ao','ao']
```
>这里就是查找`ao`后面跟个位置的模式,是不是很好理解了已经。
- 4.6`\\B`
这个也和上一个一样,是一个特殊的位置。匹配的是单词字符和单词字符之间的位置。
```
#case 1: 单纯的"\\\\B"
reStr="\\\\B"
targetStr="a _a"
re.findall(reStr,targetStr)
>>>['']
```
*解释*:这里的结果就是在下划线和`a`之间的这个位置。
```
#case 2:和其他模式搭配
reStr=r"a\\B"
targetStr="aa-aaa"
re.findall(reStr,targetStr)
>>>['a', 'a', 'a']
```
*解释*:这里模式想找的是字符`a`并且a后面还有其他的单词字符跟着。如果换成target="aa-a_a"的话,那么结果就是只有两个`a`被找到了。
>写在最后:今天是劳动节了,已经在昨天爬了香山,所以决定未来的三天假期就不出门了。早上起了很晚,慢悠悠的处理了手头上的一点工作,然后一天完成了这点文章,终于对正则表达式有了点深刻的认识。祝愿大家节日快乐,劳动人民最光荣!下一篇讲解剩余的正则表达式的知识。
### 参考
- [正则表达式-wiki](http://www.wikiwand.com/en/Regular_expression)
- [正则表达式-在线测试](http://tool.oschina.net/regex/)
- [Python中的正则表达式](http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html)
- [re - Regular expression operations](https://docs.python.org/2/library/re.html)
- [Python中正则表达式模块--`re`的使用](http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html)
网友评论