03.编程学习--正则表达式(入门一)

文:郑元春

人生苦短,我用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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容