《正则表达式必知必会》 读书笔记

regex作用:搜索、替换。
regex语言不是一种完备的程序设计语言,是内置在其他语言、软件里的迷你语言。在不同软件、语言里使用方法有所区别

同一个问题往往有多种解决方案。实践吧

正则表达式被简称为 模式。由 普通字符元字符(有特殊含义的特殊字符)组成。

. 匹配任何一个单个的字符(包括空格,唯独不包括换行符)
明确只用到.本身  就加上转义符\   组成\.
明确只用到\本身  就加上转义符\   组成\\

字符集合[]  能够与该集合里的任意一个成员相匹配
常用于大小写
字符区间0-9 A-Z a-z  [0-9a-zA-z]可以匹配任何一个字母or数字
A-z会包含一些非字母的符号
颜色和十六进制常用 a-f A-F 0-9
明确只用到[]本身  就加上转义符\   组成\[ 和 \]

取非^   取非的效果用于集合里的所有字符和区间,不仅限于紧跟在^字符后的那一个
[^0-9a-f]匹配任何不是 数字 和 a-f 的字符

元字符大致分两种:匹配文本的 and 语法要求的

匹配 非打印 空白 字符
\n 换行符 \r 回车符 \t 制表符 \v 垂直制表符 \f 换页符
\r\n匹配 回车+换行(windows用这个组合作为文本行的结束标签)
(unix和Linux只用一个\n来结束文本行)

\d 匹配任何一个数字字符(等价于[0-9])
\D 匹配任何一个非数字字符(等价于[^0-9])
\w 匹配任何一个字母 数字 下划线(大小写均可)(等价于[a-zA-Z0-9_]
\W 匹配任何一个非字母数字下划线(等价于[^a-zA-Z0-9_]
\s 匹配任何一个空白字符(等价于[\f\n\r\t\v])
\S 匹配任何一个非空白字符(等价于[^\f\n\r\t\v])

使用十六进制,用前缀\x给出  \x0A 对应ASCII字符10 等价于\n
使用八进制,用前缀\0给出  \011 对应ASCII字符9 等价于\t

有个POSIX字符类,这个知道就好。(js不支持)
比如[:alnum:] 等价于 a-zA-Z0-9
[[:alnum:]] 等价于 [a-zA-Z0-9]

+ 匹配一个 或 多个字符
明确只用到+本身  就加上转义符\   组成\+

在字符集合里, .和+这样的元字符将表示本身,不需要被转移(转义了也没坏处)
[\.] 等价于 [.]

* 匹配零个 或 多个
明确只用到*本身  就加上转义符\   组成\*
比如 匹配邮箱 \w+[\w.]*@[\w.]+\.\w+

? 匹配零个 或一个
明确只用到?本身  就加上转义符\   组成\?
比如 匹配url https?://[\w./]+

[\r]? 等价于 [\r](集合里也是可以只放一个元素的,适当添加[]可增加可读性)

{} 设定重复次数
明确只用到{}本身  就加上转义符\   组成\{ 和 \}
[0-9]{3}    重复三次
[0-9]{0,4}  重复最少0次最多四次
[0-9]{3,}   重复最少3次
比如 匹配月日年格式 \d{1,2}[-\/]\d{1,2}[-\/]\d{2,4}
( /可以加\来转义成\/,也可以不加 )
(上述例子不能检查日期是否有效比如99-99-9999,格式符合但不对,因为没有99月99日)

贪婪型与懒惰型 *和+和{n,}都是贪婪型
比如living in <b>ak</b> and <b>hi</b>
<[Bb]>.*</[Bb]> 因为贪婪,所以会从第一个<b>到最后一个</b>,有and
变懒惰型在后面加?   即*? +? {n,}?
<[Bb]>.*?</[Bb]>    只匹配ak和hi,没有and

\b 指定单词边界,开头和结尾(匹配\w和\W之间的那个位置
比如the cat scattered his food all over the room.
\bcat\b     只会匹配cat (空格是分隔单词的字符之一)
(\b匹配的是一个位置,而不是字符,所以\bcat\b只匹配cat三个字符)

单词边界:\w和\W之间
\B 匹配非单词边界的位置,比如-和空格之间,这两个都属于\W,所以不能用\b

^ 定义字符串开头(集合里是求非,集合外是匹配字符串开头)
$ 定义字符串结尾(^.$是一个合法的regex,但是没什么用)
注意:如果一个长字符串中间有回车换行,那就是两个字符串了。
比如检查</html>后面不再有任何实际内容    
</[Hh][Tt][Mm][Ll]>\s*$

(?m) 启用分行匹配模式,换行符将被视为一个字符串分割符  
(不过现在好像一直处于分行匹配模式,可以不用)

() 子表达式
比如一段Html里windows&nbsp;&nbsp;2000, and other subjects.
想找&nbsp;    不能用&nbsp;{2,}   要用(&nbsp;){2,}
nbsp non-breaking space非换行型空格
在不必要的地方加上括号()也是可以的,只是对匹配速度有点影响。

| 或
比如找一个4位数的年份
19|20|21\d{2} 会被解释为19或者20或者21\d{2},所以要加上括号
(19|20|21)\d{2} 这样就行了

子表达式允许多重嵌套,要适可而止

把必须匹配的情况考虑周全并写出一个匹配结果符合预期的regex很容易,但把不需要匹配的情况也考虑并保证完全排除在外更困难。
必须详尽地定义清楚。

比如匹配IP地址,先罗列规则
一、任何一个1位数或2位数。
二、任何一个1开头的3位数。
三、任何一个2开头,第二位是0-4的 三位数。
四、任何一个2开头,第二位是5,第三位是0-5。
五、匹配项的前后不应该有数字
(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))   原书的,会优先匹配小数
(((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))\.){3}((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))   倒过来,开头256会匹配到56
(?<=[^\d])(((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))\.){3}((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))    向后查找,解决了所有问题
(?<!\d)(((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))\.){3}((25[0-5])|(2[0-4]\d)|(1\d{2})|(\d{1,2}))     负向后查找

\1 \2 \3 定义回溯引用backreference
相当于变量,模式的后半部分引用在前半部分中定义的子表达式
比如匹配Html里所有标题
<[Hh]([1-6])>.*?</[Hh]\1>   懒惰型 \1引用([1-6])
\0可以用来代表整个regex

$1 $2 $3回溯引用可以跨模式使用 在第一个模式被匹配的子表达式可以用在第二个模式里,且可多次引用
比如在html里给地址加上链接
搜索用 (\w+[\w\.]*@[\w]+\.\w+)
替换用 <a href='mailto:$1'>$1</a>

比如排版电话号码 把313-555-1234改成(313) 555-1234
搜索用 (\d{3})-(\d{3})-(\d{4})
替换用 ($1) $2-$3

\E  结束\L或\U转换
\l  把下一个字符转为小写
\L  把\L到\E之间的字符全部转为小写
\u  把下一个字符转为大写
\U  把\U到\E之间的字符全部转为大写
比如把html标题全变成大写
搜索用 <([Hh])([1-6])>(.*?)</[Hh]\2>
替换用 <$1$2>\U$3\E</$1$2>

前后查找
比如我想把<title></title>里的内容匹配出来,但我不想要<title>
“先想办法把内容检索出来,再手动排除”这既浪费时间,又容易出错。(消费,匹配的文本会返回)
前后查找不会返回匹配的内容,而是用于确定正确的匹配位置。(不消费,匹配的文本不返回)

?= 正向前查找
比如查url的协议 有http://www.fota.com/ https://mail.forta.com/ ftp://ftp.forta.com/
.+(?=:) 意思是 用?=找到:就行,不要把他返回,只返回.+的部分(不消费)

?<= 正向后查找
比如查上面url的域名部分
(?<=\/\/).+/ 只匹配//,不消费它,最终结果里只有域名

联合前后查找
找title里的内容
(?<=(<[Tt][Ii][Tt][Ll][Ee]>)).+(?=(</[Tt][Ii][Tt][Ll][Ee]>))

?! 负向前查找
?<! 负向后查找
比如 paid $30 for 100 apples
找钱不要$   用(?<=\$)\d+
找数额     用\b(?<!\$)\d+\b     不以$开头的数值

前后查找实际上是有返回结果的,只是这个结果的字节长度永远是0,故也称为零宽度匹配。

在表达式内嵌入条件处理:回溯引用条件 和 前后查找条件


回溯引用条件
?(1)检查第一个回溯引用是否存在,存在才允许使用后面的一个表达式
?(2) ?(3) 第二个 第三个
用法1:相当于if   ?(backreference)true-regex
要匹配这个img和带有链接的img
<a href='aergag'><img src='eageg'></a>
<img src='aerg'>
(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)
当第一个子表达式存在时,才用上?(1)后面的表达式进行匹配

用法2:相当于if else  ?(backreference)true-regex|false-regex
要匹配(123)456-1234或者123-456-1234的电话号码
有干扰项(123-456-1234
(\()?\d{3}(?(1)\)|-)\d{3}-\d{4} 左括号存在则有右括号,否则用-


前后查找条件
把回溯引用?(1)里的1改成一个前后查找表达式
让不符合条件的整个都不出现在匹配结果里(这个应该用的最多?)
比如有11111
22222
33333-
44444-4444  其中33333-不符合
如果用\d{5}(-\d{4})?,虽然不会匹配到33333-的-,但还是会匹配到33333
用 \d{5}(?(?=-)-\d{4}) 意思是如果匹配到了-,就必须匹配\d{5}-\d{4},这样子33333-有-但不满足,就整个都不会出现了

 

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页