正则表达式

参考资料:

基本概念

正規表現(せいきひょうげん、英: regular expression)形式言語分野
用于文本内容的查找和替换(差し替える)
用于其他语言或者产品软件里面
在使用的时候一定要注意转义符号\,不使用这个符号的时候代表的是真实的内容,使用了才有相应的意思

正则字符

元符号

  • 被匹配的字符第一个必须和^之后的一样,最后一个必须和$之前的一样
  • “^”:
    • 匹配行或字符串的起始位置
    • 整个文档的起始位置
  • $:行或者字符串的结尾

  • \b:用于匹配边界,不消耗字符(Boundary)

    • \bis\b 用来识别is的两边是否是边界
  • \d:匹配数字(digit)
    • 比如0开头,五位数-> ^0\d\d\d\d\d$
  • \w:匹配字母,数字,下划线(基本可以理解为注册用户名的要求)
  • \s:匹配空格,\s+可以让空格重复
  • .:匹配除了转行符号以外的任何字符。\w的加强版,相当于w加上空格
  • []:匹配在空号内元素的字符,只匹配存在于括号里面的。可以写成[a-z]

反义

上面的表达写成大写,如果是[]的话变成[^],表达的意思是不包括这些的字符

量词

有关量词的三个概念:

  • 贪心 * 会首先匹配整个字符串,会选择尽可能多的内容,失败的话就backtracking(消耗最大
  • 懒惰 ?从起始位置开始尝试匹配,每次检查一个内容,直到检查完所有的内容(相当于遍历)
  • 占有 + 覆盖整个字符串,然后寻找。但是就试一次

相关量词:

  • 贪心* 会重复0次或者更多
    • ”aaaaa“里面匹配a* ,那么得到的是所有的字符a
    • 重复一次或多次:
      • ”aaaaa“,a+会取字符中所有a值,但是* 可以是0次,+不行
  • ? 重复零次或一次
    • ”aaaaa“,a?只会匹配一次,结果也是单个字符a
  • {n},重复n次,比如a{3}会匹配aaa
  • {n,m} 重复n-m次,比如a{3,4}可以匹配到aaa或者aaaa
  • {n,} 重复n次或更多次,也就是至少重复n次

懒惰限定符(大家和?的排列组合)

  • *? 重复任意次,但是尽可能少重复
    • 比如 acbacb,正则 a.*?b,只会匹配acb,因为需要.重复的数量尽可能少
  • +?重复1次或者更多次,但是尽可能少重复
  • ?? 重复0次或一次,但是尽可能少重复
  • {n,m}重复n-m次,但是尽可能少重复。比如a{0,m}?取到的是空
  • {n,}?至少重复n次,尽可能少重复

进阶

捕获分组

如果给一部分的内容加上了括号,这部分的内容就被抓住了。然后如果后面用到了相同内容的表达式,就可以直接用一个符号代替,而不用继续写一个了。
不考虑重复使用的时候,也可以单独只用于分组,分组之后的内容可以加上+ * ?等内容进行重复。但是注意嵌套层数过多会引起歧义
常用写法

  • (exp):匹配exp。捕获到自动命名的组里面,\1这样的
  • (?exp)匹配exp,捕获内容并自己命名,后面引用的时候需要 “\k
  • (?:exp):匹配exp,但是不捕获,也不给这个组分配编号
  • (?=exp):匹配exp前面的位置
    • how are you doing,正则(?.+(?=ing)),去ing前面的字符,匹配出来的是how are you do(匹配ing之前的.+)
  • (?<=exp):匹配后面的位置。比如(?(?<=how).+),匹配后面的位置,也就是匹配how之后的.+
  • (?!exp):匹配后面不跟着exp的位置。比如\d{3}(?!\d)匹配三个数字,然后后面不再跟数字了
  • (?<!exp):匹配前面不是exp的位置。(?!<[0-9])123,匹配123,并且123前面不能是数字

例子:

分组使用

比如匹配IP地址,IP地址由四部分组成,每一部分是0-255的数字。则可以分为以下的部分表示

  • 一位数字
  • 不以0开头的两位数
  • 2开头,第二位是0-4的三位数
  • 25开头,第三位是0-5的三位数
    1
    ((25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d))\.){3}(25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d))

回溯引用

比如匹配HTML里面的标题元素

1
<(h[1-6])>\w*?<\/\1>

其中,h[1-6]被分为一组,这一组的名字叫做\1,也就是说前后的两部分需要一样。h1对h1,h2对h2才能匹配上

替换(需要两个regexp)

比如修改电话号码格式
313-555-1234

  • 查找的正则式(\d{3})(-)(\d{3})(-)(\d{4})
  • 需要替换成为的格式($1)$3-$5。也就是说把第一个正则式中的第1,3,5个括号直接代入了后面替换的格式里面
  • 替换之后的内容 (313)555-1234

大小写转变

  • \l 把后面跟随的单独的字符改成小写
  • \u 把单独的字符改成大写
  • \L 把L之后,E之前的全都变成小写
  • \U 把U之后,E之前的全都变成大写
  • \E 结束符号

如,abcd,查找(\w)(\w{2})(\w),然后改为$1\U$2\E$3

前后查找

订好了应该匹配的内容的首尾内容,但是不包括这个首尾内容。也就是前面说到的向前匹配?=和向后匹配?<=.(但是js不支持向后匹配)。如果要找非的条件的时候,需要把=换成!

  • 比如匹配邮箱的@前面的部分 (\w+|.)+(?=@)(这里加上了.,因为我的学校邮箱@前面也是有. 的)
  • 匹配结果: **xu.r.aa**@m.titech.ac.jp

嵌入条件

回溯引用

判断某个表达式是否匹配,如果匹配的话继续匹配后面的条件

1
(\()?abc(?(1)\))

  • 先匹配左括号((),?来判断左括号有0个或者1个
  • ?(1)是判断的表达式,也就是说能匹配到左括号的时候,再匹配右括号。
  • 匹配结果:
    abc
    (abc)
    (abc

前后查找

条件为首尾是否匹配,如果匹配的话继续(注意首尾不包括在匹配内容里面)

1
\d{5}(?(?=-)-\d{4})

  • 首先匹配五位数字
  • (?=-)表示向前查找-,也就是对-向前查找,作为条件。如果向前查找成功了,那么继续进行后面的操作,也就是匹配一个-,然后匹配一个4位数字
    44444-4444
    44444-
    66666