[2017.02.24] 学习《正则表达式必知必会》

《正则表达式必知必会(修订版) —— Sams Teach Yourself Regualr Expressions in 10 Minutes》

简介

全球技术人员正则表达式入门首选,紧贴实战需求,让你在通勤的路上就可以掌握的编程利器。
书很薄,简明扼要,几个小时就可以看完,实例加说明的案例驱动,是不错的正则表达式入门书籍。

工具

google regular expression cheetsheet test

前言

学习正则表达式(Regular expression)关键不在于你知道多少个特殊字符,而是在于你会不会运用他们去解决实际问题。只要你清晰地理解想要解决的问题并学会如何正确使用正确表达式,那你在文本处理时,将得到一把瑞士军刀。本书讲授的正则表达式面向零起点的同学,从最简单的文本匹配开始循序渐进地向大家介绍许多复杂的专题,包括回溯引用(backreference)、条件性求值(conditional evaluation)和前后查找(lookingaround)等。本书以案例驱动,学到后的知识可以立即运用到实践中,帮助读者全面、系统、快速地掌握正则表达式并运用到解决实际问题中去。

正则表达式是一种工具,为了解决匹配和处理文本字符串这类专门的问题而被发明出来的。正则表达式的两种基本用途:搜索和替换。给定一个正则表达式,要么匹配一些文本进行搜索,要么匹配并替换一些文本进行替换。正则表达式是一种内置在其他语言或者软件中的"迷你"语言。正则表达式的语法很容易掌握,真正挑战的是如何运用那些语法把实际问题分解为一系列正则表达式并最终解决问题,必须通过亲身实践/反复刷题才能够掌握。正则表达式对文本进行匹配和处理时解法不唯一,不同的实现可以达到相同的功能,但是性能可能会有差异(如速度、准确率等)。学习正则表达式的关键在于实践、实践、再实践。

一些语法

正则表达式可以用来匹配包含字符串内容的模式(pattern)。通常,正则表达式引擎默认只返回第一个结果,为了能够匹配所有的结果,开启global标识。反斜杠(\)是一个元字符(metacharacter,表示这个字符有特殊含义而不是字符本身的含义)。
花括号表示范围,圆括号表示子表达式,方括号标示括号内的任意一个单字符。
子表达式的作用是把同一个表达式的各个相关部分组合在一起。子表达式必须使用圆括号围起来,常见用途是:对重复次数元字符的作用对象做出精确的设定和控制,对|操作符的OR条件做出准确的界定。如果有必要,还可以嵌套子表达式。
子表达式的回溯引用通常从1开始计数(\1、\2等),第0个匹配(\0)表示整个表达式。 但是这用直接以数字顺序来进行回溯引用的方式不适合用作子表达式相对位置发生变化的场景。为了弥补这一个不足,新的正则表达式实现支持“命名捕获”(named capture):给某个子表达式起一个唯一的名字,然后使用这个名字(而不是相对位置)来引用子表达式。

例1:使用回溯引用查找正确的标题

<H2> Hello <H2>  +
<H2> Hi <H3>     -
正则查找 <([hH][1-6])>[^<]*?<\1>

例2:使用回溯引用进行修改替换

313-555-1234  => (313) 555-1234
正则查找,使用了5个子表达式: (\d{3})(-)(\d{3})(-)(\d{4})
正则替换,使用回溯引用: ($1) $3-$5

例3:向前向后查找

有了正向、负向的向前向后查找,可以对最终的匹配结果内容进行更加精确地控制。前后查找操作是我能利用子表达式制定文本匹配操作发生的位置,并受到只匹配不消费的结果。
向前向后查找(不消费当前字符)
向前查找网络协议 https://www.zhihu.com
$.+(:)    => https:
$.+(?=:)  => https
向后查找标价: I paid $12.34 for 10 tomatos
(\$)[0-9.]+      => $12.34      # 子表达式引用
(?<=\$)[0-9.]+   => 12.34       # 正向后查找
\b(?<!\$)\d+\b   => 10          # 负向后查找

例4:嵌入条件的前后查找

嵌入条件前后查找的两种情况:根据回溯引用或前后查找进行条件处理。
回溯引用条件的语法定义是(?(backreference)true-regex|false-regex),表示接受一个条件和两个分别在这个条件得到满足和没有得到满足的时候执行的子表达式。

使用回溯引用条件查找正确匹配电话格式:
123-456-7890     +
(123)456-7890    +
(123) 456-7890   -
(123-456-7890    -
123 456 7890     -
1234567890       -
模式: (\()?\d{3}(?(1)\)|-)\d{3}-\d{4}
(\()? 表示 匹配一个可选的左括号,括起来得到一个子表达式
(?(1)\)|-) 是一个回溯引用条件,根据条件是否得到满足去匹配右括号)或者-。如果(1)存在也就是找到了左括号,那么就必须匹配右括号\),否则,-必须被匹配。
这样,只有配对出现的括号才被匹配;如果没有使用括号或者括号不匹配,电话号码中的区号和其余数字之间的-分隔符必须被匹配。

使用前后查找条件匹配美国邮编。
美国邮编有两种格式,一种是12345形式的ZIP格式,另一种是12345-6789形式的ZIP+4格式。只有ZIP+4格式才必须使用连字符分割前5位和后4位数字。
12345      +
12345-     -
12345-6    -
12345-6789 +
模式: \d{5}(?(?=-)-\d{4})
\d{5} 表示匹配前5个数字。
(?=-) 表示前向引用,匹配但不消费一个连字符
(?(?=-)-\d{4}) 前向查找条件,如果匹配到一个连字符,那么-\d{4}将匹配连字符和后面的4位数字。这样将不和条件的连字符排除在匹配结果之外。

例5:匹配中国固定电话号码

中国固定电话号码的规律是,最开始的一位一定是0,表示长途,接着是两到四位数组组成的区号,然后是七或者八位的电话号码,其中首尾不是1。习惯的电话格式有
029 88888888
029 8888 8888
(029)88888888
(029) 88888888
029-88888888
029-8888 8888
029-8888-8888
\(?0\d{2,4}\)?[- ]?[2-9]\d{2,3}[- ]?\d{4}

例6:匹配中国邮政编码

中国邮政编码规则是,前两位标示省、市、自治区,第三位标示邮区,第四位标示市县,第最后两位代表投递邮局。共6位数字,其中第二位不为8。
如461428
\d([0-7]|9)\d{4}

例7:匹配中国公民身份证号码

可能是15或者18位。前6位代表户口所在地编码,其中第一位是1~8;此后是出生年月日,出生年份的前两位目前自能是18\19、或20,而且是可选的(兼顾15位);最后一位校验码是数字或者X,且是可选的。
如41272119931118183X
[1-8]\d{5}((18)|(19)|(20))?\d{2}[0-1]\d[0-3]\d{4}[X\d]?

例8:匹配IP地址

IP地址是由4个字节构成,取值范围都是0~255,通常被表示成4组以点号(.)分隔开的十进制整数(每个整数有1~3位数字0~255构成)。
如127.0.0.1  251.247.180.98
0~99,100~199,200~249,250~255
(((\d{1,2})|(1\d{2})|(2\d[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2\d[0-4]\d)|(25[0-5]))

例9:匹配URL网址

简单的URL地址。对URL地址进行匹配是一个相当有难度的任务,其复杂性取决于想要获得多么精确的匹配结果。最简单情况下,URL匹配模式至少

应该包含:一个协议名(http/https/ftp)、一个主机名、一个可选的端口号、一个文件路径。更加完备的匹配还需好匹配URL的查询字符串以及可选的用户登录信息。
如:
http://jin:pwd@www.cnblogs.com:8080/blog/index.php?a=1&b=2
(http/https/ftp)://(\w*:\w*@)?[-\w.]+(:\d+)?(/([\w/_.]*)(\?\S+)?)?)?

例10:匹配注释

HTML注释必须被放在<!--和-->标签之间,这两个标签至少应该包含两个连字符。
如: <-- Page title -->
模式:<-{2,}.*?-{2,}>

C/C++/Java单行注释
如: // 这是注释
模式: //.*?

例11:更多的例子

((19|20)\d{2})   19XX或20XX
([A-Za-z0-9_]+)  字母数字下划线
(\d{1,2}[-\/]\d{1,2}[-\/]\d{4}) 日期,如23/2/2017
([^\s]+(?=\.(jpg|gif|png))\.\2)  jpg,gif or png image
(^[1-9]{1}$[1-4]{1}[0-9]{1}$^50$) 任意从1到50的数字
(#?([A-Fa-f0-9]){3}(([A-Fa-f0-9]){3})?) 有效的3位或者6位的十六进制颜色代码
((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,15}) 8-15个字符的字符串,加上至少一个大写字母,一个小写字母,和一个数字,作为密码时很有用。
(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6}) 邮箱地址
(\<(/?[^\>]+)\>)   标签 HTML Tags

正则表达式的语法表

Anchors 位置元字符
^  字符串开头,或者行首
$  字符串结尾,或者行尾
\A 字符串开头
\Z 字符串结尾
\b 单词边界(word boundary, start or end of word)
\B \b的反义
\< 单词开头
\> 单词结尾

Character Classes 特殊字符元字符
[\b]退格符(backspace)
\t  水平制表符(Tab)
\v  垂直制表符
\r  回车符(return)
\n  换行符(new line)
\f  换页符号
\c  匹配一个控制符(control character)
\s  匹配一个空白符(space)
\S  \s的反义
\d  匹配数字
\D  \d的反义
\w  匹配任意字母数字或者下划线字符
\W  \w的反义
\x  匹配一个十六进制的数
\O  匹配一个八进制的数

Posix Character Classes
[:upper:] 大写字母 upper case letters
[:lower:] 小写字母 lower case letters
[:alpha:] 所有字母
[:alnum:] 数字和字母  digits and letters
[:digit:] 数字digits
[:word:] 字母数字下划线 digits,letters,underscore
[:xdigit:] 十六进制
[:blank:] 空白符(Space abd tab)
[:cntrl:] 控制符(Control characters)

Assertions 回溯和向前查找
?= 向前查找 lookahead
?! 负向前查找 negative lookahead
?<= 向后查找 lookbehind
?!= 或 ?<! 负向后查找 negative lookbehind
?()  条件查找 conditions: if-then
?()| 条件查找 conditions: if-then-else
?#   注释 Comment


Quantifiers 数量元字符
*   >=0,任意个(0个或多个)
+   >=1,不少于1个
?   0或1个
*?  *的惰性匹配版本
+?  +的惰性匹配版本
??  ?的非贪婪版本,ungreddy
{n} 匹配n次
{n,} 匹配最少n次
{n,}? {n,}的惰性匹配版本
{m,n} 匹配至少m次,至多n次

Ranges
.        Any character except new line (\n)
(a|b)    a or b
(...)    Group,圆括号围起来表示一个子表达式
(?:...)  Passive Group
[abc]    Range (a or b or c)
[^abc]   Not a or b or c
[a-q]    Letter between a and q
[A-Q]    Upper case letter between A and Q
[0-7]    Digit between 0 and 7
\n       nth group/subpattern,使用子表达式定义回溯引用(backreference)

Pattern modifiers
g Global match
i Case-insensitive
m multiple lines
s treat string as single line
x allow comments and white space in pattern
e evaluate replacement
U ungreddy pattern
如 (?m) 分行匹配模式

《正则表达式必知必会》目录

第1章 正则表达式入门  1
    1.1 正则表达式的用途  1
    1.2 如何使用正则表达式  2
        1.2.1 用正则表达式进行搜索  3
        1.2.2 用正则表达式进行替换  3
    1.3 什么是正则表达式  4
    1.4 使用正则表达式  5
    1.5 在继续学习之前  6
    1.6 小结  6
第2章 匹配单个字符  7
    2.1 匹配纯文本  7
        2.1.1 有多个匹配结果  8
        2.1.2 字母的大小写问题  8
    2.2 匹配任意字符  9
    2.3 匹配特殊字符  12
    2.4 小结  14
第3章 匹配一组字符  15
    3.1 匹配多个字符中的某一个  15
    3.2 利用字符集合区间  17
    3.3 取非匹配  21
    3.4 小结  22
第4章 使用元字符  23
    4.1 对特殊字符进行转义  23
    4.2 匹配空白字符  26
    4.3 匹配特定的字符类别  28
        4.3.1 匹配数字(与非数字)  28
        4.3.2 匹配字母和数字(与非字母和数字)  29
        4.3.3 匹配空白字符(与非空白字符)  31
        4.3.4 匹配十六进制或八进制数值  31
    4.4 使用POSIX字符类  32
    4.5 小结  34
第5章 重复匹配  35
    5.1 有多少个匹配  35
        5.1.1 匹配一个或多个字符  36
        5.1.2 匹配零个或多个字符  39
        5.1.3 匹配零个或一个字符  41
    5.2 匹配的重复次数  43
        5.2.1 为重复匹配次数设定一个精确的值  44
        5.2.2 为重复匹配次数设定一个区间  45
        5.2.3 匹配“至少重复多少次”  46
    5.3 防止过度匹配  47
    5.4 小结  49
第6章 位置匹配  50
    6.1 边界  50
    6.2 单词边界  51
    6.3 字符串边界  54
    6.4 小结  59
第7章 使用子表达式  60
    7.1 什么是子表达式  60
    7.2 子表达式  61
    7.3 子表达式的嵌套  65
    7.4 小结  67
第8章 回溯引用:前后一致匹配  68
    8.1 回溯引用有什么用  68
    8.2 回溯引用匹配  71
    8.3 回溯引用在替换操作中的应用  74
    8.4 小结  79
第9章 前后查找  80
    9.1 前后查找  80
    9.2 向前查找  81
    9.3 向后查找  83
    9.4 把向前查找和向后查找结合起来  86
    9.5 对前后查找取非  87
    9.6 小结  89
第10章 嵌入条件  90
    10.1 为什么要嵌入条件  90
    10.2 正则表达式里的条件  91
        10.2.1 回溯引用条件  91
        10.2.2 前后查找条件  94
    10.3 小结  96
附录
    附录A 常见应用软件和编程语言中的正则表达式  97
    附录B 常见问题的正则表达式解决方案  110
    附录C 正则表达式测试器  128
posted @ 2017-02-24 18:02  起风啦  阅读(385)  评论(0编辑  收藏  举报