一 、简介

1. 正则表达式的历史

两千多年前,我国伟大的诗人、政治家 屈原 曾经说过,

帝高阳之苗裔兮,朕皇考曰伯庸。

摄提贞于孟陬兮,惟庚寅吾以降。

皇览揆余初度兮,肇锡余以嘉名。

名余曰正则兮,字余曰灵均。

屈原曰:“以我名字命名的 正则表达式当然是我发明的了!(误)”

  • 起源于1951年,数学家Stephen Cole Kleene在论文《正则集代数》中定义“正则集”,使用数学符号描述了正则语言;

  • 1970年代,Thompson把正则集应用于计算机领域,并开发了grep文本处理工具;

  • 1980年代, Perl之父(Lary Wall),树立了正则表达式的标准和地位,推动正则表达式进入稳定成熟期。

2. 正则表达式定义

正则表达式(Regular Expression,简称 re,或者regex),

描述字符串集的字符串,对字符串匹配模式的描述和规定。字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

讲人话:一种字符串高级查找技巧

3. 正则表达式的应用

应用非常广泛,如:

  • 【grep】 【awk】 【sed】 命令
  • 编辑器中,全局指定文本查找替换
  • 网络爬虫中在网页中查找url,图片url等
  • 输入框中限制用户只能输入指定的格式,比如用户名在20个字符以内,密码必须同时包含大小写特殊字符和8位密码以上
  • 等等。。。

举一些例子:

1
2
3
4
5
6
7
   匹配空行^$
   正整数^[0-9]*[1-9][0-9]*$
   匹配国内电话号码(\d{3}-|\d{4}-)?(\d{8}|\d{7})?
   E-mail地址^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$
   URL^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$
   正浮点数^((0-9)+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
   中文英文数字及下划线^[\u4e00-\u9fa5_a-zA-Z0-9]+$

二 、正则表达式的使用

看到上面密密麻麻的字符,是不是很慌? 不用着急,慢慢来。

可以使用 Sublime Text 或者 VS Code 之类的IDE进行学习实践。

基本元素

  • 文本字符:文本字符也叫基本字符、非转义字符。如0-9,a-z等
  • 元字符:就是有特殊表意的字符。下面主要就是讲解元字符。

元字符分类

1 简单字符和特殊字符

  a: 表示匹配字母 a
  ^: 行首(表示一行的开头这个位置,不匹配任何字符)
  $: 行尾(表示一行的结尾这个位置,不匹配换行符本身,在换行符之前)

例如,正则表达式 ^$ 表示空行,该行中除了换行符外没有任何字符。

2 转义字符

因为这些符号不加反斜杠转义都表示了特殊含义。

  \t: 制表符
  \n: 换行符
  \b: 一个单词的开头(不匹配任何字符,单纯表示一个位置类型)
  
  \-: 减号
  \+: 加号
  \*: 乘号
  \.: 英文点
  \\: 反斜杠
  \(: 左括号 右括弧
  \[: 左中括号 右中括号
  \{: 左花括号 右花括号

3 字符集合

  [abc]:字母a或b或c中的一个
  [a-z]:一个小写字母
  [a-zA-Z]:一个字母
  [0-9]:一个数字

  \d: 和[0-9] 含义相同 (英文:digit)
  \w:  和[A-Za-z0-9_] 注意包含下划线 含义相同:(英文:word)
  \s:  和[\t\n ] 含义相同,空白字符(英文:space)
  . :  英文点表示任意字符,不包含换行符。

4 负向匹配

[^…] 不在集合符号中的任意一个

[^a]: 不是字母a的单个符号
[^abcd]: 不是字母abcd中的任何一个
[^\d]: 不是数字的单个符号,简写:\D
[^\w]: 不是字母数字集合,简写: \W
[^\s]: 可见字符,简写 \S
[\s\S]: 任意字符,也包括换行符,同样可以表示为[\w\W],[\d\D]

5 零宽字符

也叫锚点 例如, 前面说的 行首^ 行尾$ 字符就是零宽字符,因为不匹配任何字符,只表示一种含义。

除此之外,还有其他的零宽字符:

  \b: 零宽字符,表示一个单词的边界(^\w|\w$|\W\w|\w\W) (英文单词bound)
  \B: 非单词边界

6 匹配次数

指前面字符的重复次数,跟在前面一个匹配的后面。

    ?:出现0次或1次;
    *:出现0次、1次或多次;
    +:出现1次或多次;
    {n}:出现正好n次;
    {n,}:出现n次以上;
    {n,m}:出现 n ~ m 次

7 分组匹配

用小括号将一个子模式串括起来,表示一个分组,作为一个整体。

(ac)* :匹配ac这个分组出现任意次,如匹配 acacacac

另外,使用 \1 \2 \3\9 表示匹配模式中的反向匹配, 匹配前面第k个分组匹配到的内容。注意模式和实际匹配到的内容的差别。

(?:ac): 表示跟上面一样的匹配,不过,ac并不存储在队列中,`\1`无法获取到其内容。

在替换模式中,$0, $1, $2 表示匹配到的整个字符串,第一个分组中匹配到的内容,第二个分组匹配到的内容

思考题: (a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\11匹配什么?

8 逻辑表示

逻辑表示中的与或非,其中就是前后的模式写在一起即可表达 逻辑 前面说了负向选择 逻辑,使用 | 表示

例如, cat|dog 表示匹配cat或者dog

9 贪婪匹配

正则表达式一般趋向于最大长度匹配(默认是贪婪模式) 在可变匹配次数后面加上来使用非贪婪模式。 例如:1234dddd1234 (\d{2,4}).*\1 匹配到的是: 1234dddd1234 (\d{2,4}?).*\1匹配到的是: 1234dddd12

10 零宽断言

有时,我想指定某个字符串前面的一个字符串,或者某种类型字符串后面的字符串。 例如在爬虫寻找url的时候,我只想寻找 href=" 后面出现的url,怎么办?

也有叫做“环视”,环视两侧的意思。

这个其实就有一个预匹配(预搜索)的问题。

  • 正向匹配

(?<=pattern1)pattern(?=pattern2)

其中,pattern2叫做先行断言,pattern1叫做后行断言。

指的就是匹配pattern1和pattern2中间匹配pattern的字符串。

  • 负向匹配 (?<!pattern1)pattern(?!pattern2)

有正就有负,负向匹配就是不在这些模式之间的。

注意:JS的正则实现中,只支持先行断言。

11 特殊语法

一些特殊写法的语法,不是所有的实现都支持,如Javascript是不支持的。很少很少常用,如果真的需要使用的时候再去查询即可。

\p{P}: (英文property)例如\p{Digit}表示数字,等

附录:

元字符表

正则表达式的常规使用

练习题

  匹配空行:
  匹配正整数:
  匹配正浮点数:
  匹配国内电话号码:
  匹配E-mail地址:

  匹配中国手机号:
  匹配yyyy-MM-dd日期格式(假设每个月都都有31天):
  匹配IPv4:
  匹配jpg或者png图片URL:
  匹配C语言变量名:
  匹配C语言注释:

部分示例参考答案:

cell_phone = ^(?:\+?86)?1[3-9]\d{9}$
date = [1-2][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])

jpg_png_url = https?://.+?\.(jpg|png)\b

c_variable = [a-zA-Z_]\w*

dec_octet = "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
ipv4_address = "(" + dec_octet + r"\.){3}" + dec_octet

comment = (/\*([^*]|[\r\n\s]|(\*+([^*/]|[\r\n\s])))*\*+/)|(//.*)|(/\*.*)

三、通配符

类似于正则表达式,但是简单非常多,主要用在于linux命令行中。

常用的通配符(shell,无匹配次数,位置)

   *:  匹配 0 或多个字符;  
   ?:  匹配任意一个字符;
  [list]: 匹配 list 中的任意单一字符
  [!list]或[^list]: 匹配除list 中的任意单一字符
  [c1-c2]:     匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z]
  [!c1-c2]或[^c1-c2]:     匹配不在c1-c2的任意字符 
  {string1,string2,...}:  匹配 sring1 或 string2 (或更多)其一字符串 

练习题: 1.使用ls显示当前目录所有的.c和.cpp文件 2.使用ls显示当前目录以数字开头所有不以小写字符结尾的全部文件
3.使用ls显示当前目录以数字开头所有不以小写字符结尾且中间不带下划线的全部文件