正则表达式

正则表达式入门

  1. 名称:正则表达式[regular expression,简称RegEx]

  2. 使用正则表达式
    搜索或者替换。匹配一些文本(进行一次搜索)。匹配并替换一些文本(进行一次替换)。

  3. 定义:正则表达式是一些用来匹配和处理文本的字符串。

  4. 语法:语法是正则表达式最容易掌握的部分,真正的挑战是学会如何运用那些语法把实际问题分解为一系列正则表达式并最终解决。

匹配单个字符

  1. 匹配纯文本
    匹配内容与正则表达式内容一致:只能匹配一个结果
    正则表达式是区分字母大小写的。
  2. 匹配任意字符
    .字符(英文句号)可以匹配任何一个单个的字符;可以匹配任何单个的字符、字母、数字甚至是.字符本身。
  3. 匹配特殊字符
    .字符在正则表达式里有着特殊的含义。
    如果想要匹配.本身,必须在.的前面加上一个\(反斜杠)字符来对它进行转义。
    \是一个元字符(metacharacter,表示“这个字符有特殊含义,而不是字符本身含义”)。
    在绝大多数的正则表达式实现里,.只能匹配除换行符以外的任何单个字符。

匹配一组字符

  1. 匹配多个字符中的某一个
    使用元字符[]来定义一个字符集合。
    这两个字符之间的所有字符都是该集合的组成部分,字符集合的匹配结果是能够与该集合里的任意一个成员相匹配的文本。
    字符集合在不需要区分字母大小写(或者是只需匹配某个特定部分)的操作里比较常见。例如:[Rr]eg[Ee]x
  2. 利用字符集合区间
    匹配任意一个数字:[0123456789]
    字符区间可以用-(连字符)来定义,模式[0-9]的功能和[0123456789]完全等价
    [A-Z]匹配从A到Z的所有大写字母
    [a-z]匹配从a到z的所有小写字母
    [A-F]匹配从A到F的所有大写字母
    [A-z]匹配从ASCII字母A到ASCII字母z的所有字母。其中还包含[^等在ASCII字符表里排序在Z和a之间的字符。
    字符区间的首、尾字符可以是ASCII字符表里的任意字符。
    在定义一个字符区间的时候,一定要避免让这个区间的尾字符小于它的首字符。
    在字符集合以外的地方,-只是一个普通字符,只能与-本身相匹配。
    匹配任何一个字母或数字:[A-Za-z0-9],完整的模式:[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789]
    匹配RGB色值:#[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]
  3. 取非匹配
    除了那个字符集里的字符,其他字符都可以匹配。
    元字符^表示对一个字符集合进行取非匹配。
    [^0-9]表示任何一个不是数字的字符。
    ^的效果将作用于给定字符集合里的所有字符或字符区间。

使用元字符

  1. 对特殊字符进行转义

    匹配myArray[0]myArray\[0\]\[匹配[\]匹配]
    匹配myArray[0],数字从0到9:myArray\[[0-9]\]
    匹配\本身:需要对本身转义\\
    在一个完整的正则表达式里,字符\后面永远跟着另一个字符

  2. 匹配空白字符
    元字符大致分为两种:一种是用来匹配文本的(比如.),另一种是正则表达式的语法要求的(比如[])。
    空白元字符表:

元字符 说明
[\b] 回退(并删除)一个字符(Backspace键)
\f 换页符
\n 换行符
\r 回车符
\t 制表符(Tab键)
\v 垂直制表符
\r\n匹配一个“回车+换行”组合
\r\n\r\n匹配两个连续的行尾标签,即可匹配两条记录之间的空白行。
3. 匹配数字(与非数字)
数字元字符:
元字符 说明
\d 任何一个数字字符(等价于[0-9]
\D 任何一个非数字字符(等价于[^0-9]
3. 匹配字母和数字(与非字母和数字)
字母数字元字符:
元字符 说明
\w 任何一个字母数字字符(大小写均可)或下划线字符(等价于[a-zA-Z0-9_]
\W 任何一个非字母数字或非下划线字符(等价于[^a-zA-Z0-9_]
4. 匹配空白字符(与非空白字符)
空白字符元字符:
元字符 说明
\s 任何一个空白字符(等价于[\f\n\r\t\v]
\S 任何一个非空白字符(等价于[^\f\n\r\t\v]
5. 匹配十六进制或八进制数值
十六进制(逢16进1)数值要用前缀\x来给出。
\x0A对应于ASCII字符10(换行符),其效果等价于\n
八进制(逢8进1)数值要用前缀\0来给出。
\011对应于ASCII字符9(制表符),其效果等价于\t
6. 使用POSIX字符类

重复匹配

  1. 匹配一个或多个字符
    匹配同一个字符(或字符集合)的多次重复
    +匹配一个或多个字符(至少一个;不匹配零个字符的情况)
    \w+匹配一个或多个字母数字字符
    [\w.]匹配一个或多个(字母、数字、下划线和.)字符
  2. 匹配零个或多个字符
    *的用法和+完全一样,用来匹配连续出现零次或多次的情况
    匹配*本身:\*
  3. 匹配零个或一个字符
    ?只能匹配?前一个字符(或字符集合)的零次或一次出现
  4. 匹配的重复次数
    重复次数(interval)要用{}字符来给出,把重复次数数值写在它们之间。
    为重复次数设定一个精确的值:#[a-fA-F0-9]{6}
    为重复匹配次数设定一个区间:[\d]{2, 4},连续出现最少2个最多4个数字
    重复次数可以是0:[\d]{0, 3},表示重复次数可以是0、1、2、3
    [\d]{3, }:表示必须重复3次或者更多次
  5. 防止过度匹配
    *+都是所谓的“贪婪型”元字符
    懒惰性元字符:只要给贪婪型元字符加上一个?后缀即可
    匹配图片地址链接:http.+?\.jpg|png
    常用的贪婪型元字符和它们的懒惰性版本:
贪婪新元字符 懒惰性元字符
* *?
+ +?
{n, } {n, }?
懒惰性元字符:就近原则

位置匹配

  1. 单词边界
    \b:单词边界的限定符,用来匹配一个单词的开头或结尾。
    位置:一个能够用来构成单词的字符(字母、数字和下划线,也就是与\w相匹配的字符)和一个不能用来构成单词的字符(也就是与\W相匹配的字符)之间。
    \B:表明不匹配一个单词边界。
    匹配前后多余空格的连字符:\B-\Bcolor - coded其中的-
  2. 字符串边界
    用来定义字符串边界的元字符有两个:一个用来定义字符串开头的^,一个用来定义字符串结尾的$
    匹配合法的XML文档:^\s*<?xml.*\?>
    匹配一份Web页面的末尾:<[Hh][Tt][Mm][Ll]>\s*$
  3. 分行匹配模式
    (?m):分行匹配模式(multiline mode),使用时,必须出现在整个模式的最前面。
    匹配一段JavaScript代码里的所有注释内容:(?m)\s*//.*$

使用子表达式

  1. 子表达式
    (&nbsp;){2, }:匹配两个以上“不是换行符的空格”。
    子表达式必须用()括起来。
    |:或操作
    19|20:匹配数字序列1920
  2. 子表达式的嵌套
    子表达式允许嵌套
    有句话需要牢记:把必须匹配的情况考虑周全并写出一个匹配结果符合预期的正则表达式很容易,但把不需要匹配的情况也考虑周全并确保它们被排除在匹配结果以外往往要困难得多。
    匹配IP:(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(23[0-5]))\.){3}(\d{1,2})|(1\d{2})|(2[0-4]\d)|(23[0-5]))

回溯引用:前后一致匹配

  1. 回溯引用匹配
    模式的后半部分引用在前半部分中定义的子表达式
    [ ]+(\w+)[ ]+\1\1代表模式里的第一个子表达式
    <[Hh]([1-6])>(.*?)</[Hh]\1>:匹配HTML文件中所有的标题
    回溯引用匹配通畅从1开始计数(\1\2等)。在许多实现里,第0个匹配(\0)可以用来代表整个正则表达式。

  2. 回溯引用在替换操作中的应用
    替换操作需要用到两个正则表达式:一个用来给出搜索模式,另一个用来给出匹配文本的替换模式。
    回溯引用可以跨模式使用,在第一个模式里被匹配的子表达式可以用在第二个模式里。
    把一段文本里的电子邮件地址全部转换为可点击:Hello, shadow000902@gmail.com is my email address.

    1
    2
    3
    4
    # 匹配出电子邮件地址
    (\w+[\w.]*@[\w\.]+\w+)
    # 替换
    <A HREF="mailto:$1">$1</A>

    结果:Hello, <A HREF="mailto:shadow000902@gmail.com">shadow000902@gmail.com</A> is my email address.
    回溯引用语法在不同的正则表达式实现里有很大差异:JavaScript用户需要用$来替换\;ColdFusion用户在查找和替换操作里都必须使用\
    同一个子表达式可以被多次引用,只要在需要它的地方写出回溯引用就行了。
    在对文本进行重新排版的时候,把文本分解成多个子表达式的做法往往非常有用,这可以让我们对文本的排版效果做出更精确的控制。
    回溯应用实例

    对目录下的文件在``/usr/bin``目录下创建软连接
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    cd /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin
    ll | awk {'print $9'}
    # 输出
    appletviewer
    extcheck
    idlj
    jar
    jarsigner
    java
    javac
    javadoc
    javafxpackager
    javah
    javap
    正则表达式语句
    1
    2
    3
    4
    5
    6
    # 匹配一段内容
    (.+)\n
    # 替换
    /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/$1 /usr/bin/$1\n
    # 在原文中替换的正则语句(不可用。。。)
    sed -ig "s/(.+)\n//Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/$1 /usr/bin/$1\n/g" $file_name
  3. 大小写转换
    用来进行大小写转换的元字符

元字符 说明
\E 结束\L\U转换
\l 把下一个字符转换为小写
\L \L\E之间的字符全部转换为小写
\u 把下一个字符转换为大写
\U \U\E之间的字符全部转换为大写

把HTML文件中的一级标题(<H1>...</H>)的标题文字转换为大写:

1
2
3
4
# 匹配出一级标题
(<[Hh]1>)(.*?)(</[Hh]1>)
# 替换
$1\U$2\E$3

前后查找

  1. 向前查找
    向前查找指定了一个必须匹配但不在结果中返回的模式
    一个向前查找模式:其实就是一个以?=开头的子表达式,需要匹配的文本跟在=之后。
    .+(?=:):匹配出http://www.baidu.com中的http,匹配出了:,但是并没有展示出来
    向前查找(和向后查找)匹配本身其实是有返回结果的,只是这个结果的字节长度永远是0而已。因此,前后查找操作有时也被称为零宽度(zero-width)匹配操作。
    在同一个搜索模式里,可以使用多个向前查找表达式。
  2. 向后查找
    ?<=:向后查找操作符。查找出现在被匹配文本之前的字符(但不消费它)。
    (?<=\$)[0-9.]+:匹配出ABC01: $23.45中的23.45,但不匹配出01(?<=\$)只匹配出来$,但不消费它。
  3. 把向前查找和向后查找结合起来🔥
    (?<=\<[Tt][Ii][Tt][Ll][Ee]>).*(?<=</[Tt][Ii][Tt][Ll][Ee]>):匹配出<TITLE>I AM TAOYI.</TITLE>中的I AM TAOYI.
    (?<= ).+?(?=\=):匹配出spuId= skuId=中的spuIdskuId
    (?<=\=).+?(?<= ):匹配出spuId=${spuId} skuId=${skuId}中的${spuId}${skuId},当然,也可以使用\$\{.+?\}匹配出来相似的结果。
    (?<=data-url=")(.+)(?<=">):匹配出data-url="http://tool.lu/">中的http://tool.lu/
    (?<=border-for-item" title=")(.+)(?<="):匹配出class="border-for-item" title="站长工具"中的站长工具
  4. 对前后查找取非
    前后查找必须用!来取非(它将替换掉=
    各种前后查找操作符
操作符 说明
(?=) 正向前查找
(?!) 反向前查找
(?<=) 正向后查找
(?<!) 反向后查找

实践

1.jsonPostMan``bulk Edit格式

1
2
{?\n\s*"([^"]+)"\s*:\s*"?([^,"]+)"?\s*,?}?
$1:$2\n
  1. charles抓包Request中的Form参数中的\t\n转化为字符形式的\t\n
    1
    2
    (.*)[\t](.*)\n
    $1\\t$2\\n