基础正则表达式

412 阅读6分钟

正则学习

[TOC]

元字符:^ $ . [ ] { } - ? * + ( ) | \ 原义字符:除元字符外,其他所有字符都被认为是原义字符。个别情况下\会被用来创建元序列,元字符也可以被转义为原义字符。

元字符

1. 任何字符(.

被用来匹配任意字符,在正则表达式中包含它,它将会匹配在此位置任意一个字符。(除\n``\r之外)

eg:

  • '.123' -> 表示匹配项至少 4 个字符,其中匹配的字符中至少有'123'三个连贯字符,并且'123'前面需要有一个任意字符。
  • '..123' -> 表示匹配项至少 5 个字符,其中匹配的字符中至少有'123'三个连贯字符,并且'123'前面需要有两个任意字符。

2. 锚点(^ & $

在正则表达式中,插入^$被看作是锚点,表示以什么开头以什么结尾。 当^在元字符[]中时,表示的是取反

eg:

  • '^123' -> 以'123'开头的内容
  • '123$' -> 以'123'结尾的内容
  • '^123&' -> 匹配'123'字符串
  • '^..j.r$' -> 表示字符长度为5,任意字符开头,第3个字符为j, 第5个字符为r,并且以r字符结尾
  • '[^bg]123' -> 表示'123'字符前面有一个除了bg之外的任意字符

3. 中括号表达式和字符类([]

除了给定位置匹配任意字符外,通过中括号表达式可以从一个给定的字符集合中匹配单个字符

eg:

  • '[bg]123' -> 表示'123'字符前的一个字符只能是bg

一个字符集合可以包含任意多个字符,并且元字符[]中会失去它们的特殊含义。但是,^-[]中有另外的含义。 ^:表示否定 -:表示一个字符范围,传统范围为'[0123456789]',使用-为'[0-9]'

eg:

  • '[^bg]123' -> 表示'123'字符前面有一个除了bg之外的任意字符
  • '[0-9]123' -> 表示'123'字符前面有一个0123456789中的任意一个字符

3.1 POSIX 字符集

不同的系统,可能'[A-Z]'表示的字符集不同。 为了部分的解决这个问题,posix标准引入了locale概念,它能针对不同地区选择合适的字符集。

字符集说明
[:alnum:]字母数字字符。在 ASCII 中,等价于:[A-Za-z0-9]
[:word:]与[:alnum:]相同, 但增加了下划线字符。
[:alpha:]字母字符。在 ASCII 中,等价于:[A-Za-z]
[:blank:]包含空格和 tab 字符。
[:cntrl:]ASCII 的控制码。包含了0到31,和127的 ASCII 字符。
[:digit:]数字0到9
[:graph:]可视字符。在 ASCII 中,它包含33到126的字符。
[:lower:]小写字母。
[:punct:]标点符号字符。在 ASCII 中,等价于:[-!”#$%&'()*+,./:;<=>?@[\]_`{}~]
[:print:]可打印的字符。在[:graph:]中的所有字符,再加上空格字符。
[:space:]空白字符,包括空格、tab、回车、换行、vertical tab 和 form feed.在 ASCII 中, 等价于:[ \t\r\n\v\f]
[:upper:]大写字母。
[:xdigit:]用来表示十六进制数字的字符。在 ASCII 中,等价于:[0-9A-Fa-f]

eg:

  • '^[[:digit:]]123' -> 表示以数字0到9开头,后面跟着'123'

4. 交替,操作(

允许从一系列表达式之间选择匹配项,就像[]允许从一系列指定字符之间匹配单个字符|允许从一系列字符串或者其它的正则表达式中选择匹配项。和符号语义有点相通。

eg:

  • '^(bz|gz|zip)' -> 表示匹配以'bz'或'gz'或'zip'开头的字符内容

5. ? - 匹配0个或者1个元素

表示一个可选的字符。使前面的元素可有可无

eg:

  • '^1e?23$' -> 表示以'1'开头,'23'结尾,中间有或者没有'e'

6. * - 匹配0个或者多个元素

?一样,表示一个可选的字符,但是?匹配的字符只可以出现一次,而*匹配的字符可以出现任意次

eg:

  • '^1(3|5)*.$' -> 表示以'1'开头,任意字符结尾,中间可以有无数(或者0)个'3'或'5'

7. + - 匹配1个或者多个元素

*类似,但是它要求前面至少出现一次匹配的元素

eg:

  • '^1(3|5)+.$' -> 表示以'1'开头,任意字符结尾,中间至少有1个'3'或'5'

*+后面添加?可以实现最小匹配。 eg: 使用'<.*?>' 匹配'<h1>test</h1>'时,如果没有?则匹配整个字符串,如果有?则只匹配最前面的'<h1>'

8. {} - 匹配特定个数的元素

{}备用来表示要求匹配的最小和最大数目,他们通过4种方法来指定

限定符意思
{n}前面的元素出现了 n 次(前面的元素== n)
{n,m}前面的元素至少出现了n次,小于等于m次(n<=前面的元素<=m)
{,m}前面的元素出现了不多于m次(前面的元素<=m)(在iOS中需写成{0,m},其他语言未知)
{n,}前面的元素至少出现了n次(前面的元素>=n)

eg:

  • '^1(3|5){4}.$' -> 表示以'1'开头,任意字符结尾,中间有4个'3'或'5'
  • '^1(3|5){4,6}.$' -> 表示以'1'开头,任意字符结尾,中间有4到6个'3'或'5'
  • '^1(3|5){,4}.>表示1开头,任意字符结尾,中间0435iOS中需写1(35)0,4.' -> 表示以'1'开头,`任意字符`结尾,中间`有0到4个`'3'或'5'。**iOS中需写成'^1(3|5){0,4}.'**
  • '^1(3|5){4,}.$' -> 表示以'1'开头,任意字符结尾,中间有4个及以上个'3'或'5'

9. 定位符(\b & \B & ^ & $

^$第二点-锚点 \b:匹配单词边界处。 \B\b的反义,匹配单词的非边界处。 \b\B,放在字符前面表示开头,如'\bst','\Bst',放在字符后面表示结尾,比如'st\b','st\B'

eg:

  • '\bst' -> 表示语句中单词以'st'开头的部分,用其匹配 'studyst tests stdy',并将满足条件的部分替换成'#',将得到'#udyst tests #dy',其中'#udyst tests #dy'亮色部分不变。
  • 'st\b' -> 表示语句中单词以'st'结尾的部分,用其匹配 'studyst tests stdy',并将满足条件的部分替换成'#',将得到'study# tests stdy',其中'study# tests stdy'亮色部分不变。
  • '\Bst' -> 表示语句中单词不以'st'开头的部分,用其匹配 'studyst tests stdy' ,并将满足条件的'st'替换成'#',将得到'study# te#s stdy',其中'study# te#s stdy'亮色部分不变。
  • 'st\B' -> 表示语句中单词不以'st'结尾的部分,用其匹配 'studyst tests stdy' ,并将满足条件的'st'替换成'#',将得到'#udyst te#s #dy',其中'#udyst te#s #dy'亮色部分不变。

普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

\s\S

\s:匹配所有空白符号包括换行符 \S:匹配非空字符,不包括换行符

eg:

  • '^.111[\s]$':表示以任意字符开头,中间 3 个'1',以空字符串' '、'\n'或'\r'结尾。
  • '^.111[\s]$':表示以任意字符开头,中间 3 个'1',以空字符串' '、'\n'或'\r'以外的字符结尾。
  • '^.111[\s\S]$':表示以任意字符开头,中间 3 个'1',以任意字符结尾,包括空字符串' '和'\n'。

\w

匹配字母、数字、下划线。等价于[A-Za-z0-9_]

非打印字符

字符描述
\cx匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\f匹配一个换页符。等价于 \x0c 和 \cL。
\n匹配一个换行符。等价于 \x0a 和 \cJ。
\r匹配一个回车符。等价于 \x0d 和 \cM。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。
\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t匹配一个制表符。等价于 \x09 和 \cI。
\v匹配一个垂直制表符。等价于 \x0b 和 \cK。

常见的简写形式

正则表达式匹配区间记忆方式
\d[0-9]表示是一位数字其英文是digit(数字)
\D[^0-9]表示除数字外的任意字符
\w[0-9a-zA-Z_]表示数字、大小写字母和下划线w是word的简写,也称单词字符
\W[^0-9a-zA-Z_]非单词字符
\s[ \t\v\n\r\f]表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符s是space character的首字母
\S[^ \t\v\n\r\f]非空白符
.[^\n\r\u2028\u2029]通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外

正则查询

以 iOS 中的方法为例

/// 正则匹配
///
/// - Parameters:
///   - regex: 匹配规则
///   - validateString: 匹配对test象
/// - Returns: 返回结果
func RegularExpression(regex:String,validateString:String) -> [String]{
    do {
        let regex: NSRegularExpression = try NSRegularExpression(pattern: regex, options: [])
        let matches = regex.matches(in: validateString, options: [], range: NSMakeRange(0, validateString.count))
            
        var data:[String] = Array()
        for item in matches {
            let string = (validateString as NSString).substring(with: item.range)
            data.append(string)
        }
        
        return data
    }
    catch {
        return []
    }
}

/// 字符串的替换
///
/// - Parameters:
///   - validateString: 匹配对象
///   - regex: 匹配规则
///   - content: 替换内容
/// - Returns: 结果
func replace(validateString:String,regex:String,content:String) -> String {
    do {
        let RE = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
        let modified = RE.stringByReplacingMatches(in: validateString, options: .reportProgress, range: NSRange(location: 0, length: validateString.count), withTemplate: content)
        return modified
    }
    catch {
        return validateString
    }  
}

选择 () ?= ?<= ?! ?<!

() 表示捕获分组

?=

exp1(?=exp2): 查找 exp2 前面的 exp1

eg:

  • 使用 'test(?=[\d+])' 匹配字符串 '1234test12test122test', 得到的只有前面2个'test',最后一个'test'无法匹配时因为后面没有满足'[\d+]'条件

?<=

(?<=exp2)exp1:查找 exp2 后面的 exp1

eg: 使用 '(?<=[\d+])test' 匹配字符串 'test1234test12test122test', 可以得到后面 3 个'test',第一个'test'无法匹配时因为后面没有满足'[\d+]'条件。 [\d+]不知道为什么不能使用[0-9]+这种格式

?!

exp1(?!exp2):查找后面不是 exp2 的 exp1

eg: 使用 'test(?![0-9]+)' 匹配字符串 '1234test-12test122test',可以得到第 1 和 3 个'test',第 2 个'test'因为后面是数字,所以不满足。

?<!

(?<!exp2)exp1:查找前面不是 exp2 的 exp1

eg: 使用 '(?<![\d+])test' 匹配字符串 '1234test12test122-test',可以得到第 3 个'test',第 1 和 2 个'test',因为前面是数字,所以无法满足要求。[\d+]不知道为什么不能使用[0-9]+这种格式

** 反向引用 **

在正则表达式本身中引用之前出现的分组,即反向引用

eg: 比如要写一个正则支持匹配如下三种日期格式:

  • 2016-06-12
  • 2016/06/12
  • 2016.06.12

如果使用 '\d{4}(-|/|.)\d{2}(-|/|.)\d{2}' 正则去匹配,则会导致 '2016-06/12' 也匹配成功,和要求不符。

如果我们要求分割符前后一致的话,就可以使用反向引用即正则表达式为:'\d{4}(-|/|.)\d{2}\1\d{2}',其中\1,表示引用之前的分组(-|\/|\.)。 同理,\2,\3分别表示第二个分组和第三个分组

参照文档