正则是啥


正则就是用有限的符号,表达无限的序列,殆已!

正则表达式的语法一般如下(js),两条斜线中间是正则主体,这部分可以有很多字符组成;i部分是修饰符,i的意思表示忽略大小写

/^abc/i

正则定义了很多特殊意义的字符,有名词,量词,谓词等,下面逐一介绍

简单字符


没有特殊意义的字符都是简单字符,简单字符就代表自身,绝大部分字符都是简单字符,举个例子

/abc/ // 匹配 abc
/123/ // 匹配 123
/-_-/ // 匹配 -_-
/海镜/ // 匹配 海镜

转义字符


\是转义字符,其后面的字符会代表不同的意思,转义字符主要有三个作用:

  1. 是为了匹配不方便显示的特殊字符,比如换行,tab符号等
  2. 正则中预先定义了一些代表特殊意义的字符,比如\w
  3. 在正则中某些字符有特殊含义(比如下面说到的),转义字符可以让其显示自身的含义

下面是常用转义字符列表:

\n 匹配换行符
\r 匹配回车符
\t 匹配制表符,也就是tab健
\v 匹配垂直制表符
\x20 20是2位16进制数字,代表对应的字符
\u002B 002B是4位16进制数字,代表对应的字符
\w 匹配任何一个字母、数字、下划线
\W 匹配任何一个除字母、数字、下划线之外的字符
\s 匹配空白字符,入空格、tab等
\S 匹配非空白字符
\d 匹配数字字符,0~9
\D 匹配非数字字符
\b 匹配单子的边界
\B 匹配非单词边界
\ 匹配 \ 本身

字符合集


有时我们需要匹配一类字符,字符集可以实现这个功能,字符集的语法用[``]分隔,下面的代码能够匹配a或b或c

[abc]

如果要表示字符很多,可以使用-表示一个范围内的字符,下面两个功能相同

[012345678]
[0-8]

在前面添加^,可表示非的意思,下面的代码能够匹配a``b``c之外的任意字符

[^abc]

其实正则还内置了一些字符集,在上面的转义字符有提到,下面给出内置字符集对应的自定义字符集

  • . 匹配除了换行符(\n)以外的任意一个字符 = \n
  • \w = [0-9a-zA-Z_]
  • \W = 0-9a-zA-Z_
  • \s = [ \t\n\v]
  • \S = \t\n\v
  • \d = [0-9]
  • \D = 0-9

量词


如果我们有三个苹果,我们可以说自己有个3个苹果,也可以说有一个苹果,一个苹果,一个苹果,每种语言都有量词的概念

如果需要匹配多次某个字符,正则也提供了量词的功能,正则中的量词有多个,如?+*{n}{m,n}{m,}

{n}`匹配n次,比如`a{2}`,匹配`aa,'a1a',则不会匹配
{m, n}`匹配m-n次,优先匹配n次,比如`a{1,3}`,可以匹配`aaa`、`aa`、`a
{m,}`匹配m-∞次,优先匹配∞次,比如`a{1,}`,可以匹配`aaaa...
?`匹配0次或1次,优先匹配1次,相当于`{0,1}
+`匹配1-n次,优先匹配n次,相当于`{1,}
*`匹配0-n次,优先匹配n次,相当于`{0,}

正则默认和人心一样是贪婪的,也就是常说的贪婪模式,凡是表示贪婪的量词,都优先匹配上限而不是下限

字符边界


有时我们会有边界的匹配要求,比如以xxx开头,以xxx结尾

^[]外表示匹配开头的意思

^abc // 可以匹配abc,但是不能匹配aabc

$表示匹配结尾的意思

abc$ // 可以匹配abc,但是不能匹配abcc

上面提到的\b表示单词的边界

abc\b // 可以匹配 abc ,但是不能匹配 abcc

以xxx开头xxx结尾

/^138.*5779$/  //可以匹配 '138.....5779'
/\b138.*5779\b/  //可以匹配 '138.....5779'

选择表达式


有时我们想匹配x或者y,如果x和y是单个字符,可以使用字符集,[abc]可以匹配abc,如果x和y是多个字符,字符集就无能为力了,此时就要用到分组

正则中用|来表示分组,a|b表示匹配a或者b的意思

/123|456|789/  // 匹配 123 或 456 或 789

分组与引用


分组是正则中非常强大的一个功能,可以让上面提到的量词作用于一组字符,而非单个字符,分组的语法是圆括号包裹(xxx)

(abc){2} // 匹配abcabc,不匹配 abc1abc,得连续起来

分组不能放在[]中,分组中还可以使用选择表达式

(123|456){2} // 匹配 123123、456456、123456、456123

和分组相关的概念还有一个捕获分组和非捕获分组,分组默认都是捕获的,在分组的(后面添加?:可以让分组变为非捕获分组,非捕获分组可以提高性能和简化逻辑

'123'.match(/(?:123)/) // 返回 ['123']
'123'.match(/(123)/)  // 返回 ['123', '123']

和分组相关的另一个概念是引用,比如在匹配html标签时,通常希望<xxx></xxx>后面的xxx能够和前面保持一致

引用的语法是\数字,数字代表引用前面第几个捕获分组,注意非捕获分组不能被引用

<([a-z]+)><\/\1> // 可以匹配 `<span></span>` 或 `<div></div>`等

预搜索


如果你想匹配 xxx 前不能是 yyy,或者 xxx 后不能是 yyy,那就需要用到预搜索

js 只支持正向预搜索,也就是 xxx 后面必须是 yyy,或者 xxx 后面不能是 yyy

1(?=2) // 匹配12,不能匹配 21
1(?!2) // 匹配 13,不匹配 12

修饰符


默认正则是区分大小写的,这可能并不是我们想要的,正则提供了修饰符的功能,语法如下:

/xxx/gi // 最后面的 g、i 是两个修饰符
  • g 正则正常遇到第一个匹配成功的字符就会结束,加上全局修饰符 g, 可以让其匹配到结束

  • i 正则默认是区分大小写的,i 可以忽略大小写

  • m 正则默认情况下,^$ 只能匹配字符串的开始和结尾, m 修饰符可以让 ^$ 匹配行首和行尾,多行匹配

          /jing$/ // 能匹配 'asdjing', 不能匹配 'asdasdjing\n'
          /jing$/m // 能匹配 'asdjing', 也能匹配 'asdasdjing\n'
    

图形工具


有时我们会遇到特别复杂的正则,有时候可能不太直观,下面推荐一个图形化展示的工具,我们把涉及到的语法罗列一下

图形工具

javascript中的正则


在js中创建正则有两种方法,字面量和new,和创建其他类型变量一样

var reg = /abc/g // 字面量
var reg = new RegExp('abc', 'g') // new 方式,意思和上面一样

js中用到正则的地方有两个入口,正则的api和字符串的api,RegExp#test等于RegExp.prototype.test

  • RegExp#test
  • RegExp#exec
  • String#search
  • String#match
  • String#split
  • String#replace

RegExp#test


每个正则实例都有test方法,test参数是字符串,返回值是布尔值,表示当前正则是否能匹配指定的字符串

/abc/.test('abc') // true
/abc/.test('aaa') // false

RegExp#exec


exec使用方法和test一样,只是返回值并不是布尔值,而是返回匹配的结果 匹配成功返回一个数组,数组第一项是匹配结果,后面一次是捕获的分组

/abc(d)/.exec('abcd') // ['abcd', 'd', index: 0, input: 'abcd']

此数组还有另外两个参数,input是输入的字符串,index 表示匹配成功的序列在输入字符串中的索引位置 如果有全局参数(g),第二次匹配将从上次匹配结束时继续

var r1 = /ab/
r1.exec('ababab') // ['ab', index: 0]
r1.exec('ababab') // ['ab', index: 0]

var r2 = /ab/g
r2.exec('ababab') // ['ab', index: 0]
r2.exec('ababab') // ['ab', index: 2]
r2.exec('ababab') // ['ab', index: 4]

这一特性可以被用于循环匹配,比如统计字符串中abc的次数

var reg = /abc/g
var str = 'abcabcabcabc';
var num = 0;
while (reg.exec(str) !== null) {
    num++
}
console.log(num); // 4

如果匹配失败则返回null

/abc(d)/.exec('abc') // null

String#search


search 方法返回匹配成功位置的索引,参数时字符串或正则,返回结果时索引

'abc'.search(/abc/) // 0
'abc'.search('c') // 2

如果匹配失败,则返回 -1

'abc'.search(/d/) // -1
'abc'.search('d') // -1

String#match


match 方法也会返回匹配的结果,匹配结果和exec类似

'abc'.match(/abc) // ['abc', index: 0, input: abc]
'abc'.match(/abd/) // null

如果有全局参数(g),match 会返回所有的结果,并且没有index和input属性

'abcabcabc'.match(/abc/g) // ['abc', 'abc', 'abc']

String#split


字符串的split方法,可以用指定符号分割字符串,并返回数据

'a,b,c'.split(',') // [a, b, c]

其参数也可以使一个正则,如果分隔符有多个时,就必须用正则

const str = 'a,b.c*d'
str.split(/,|\*|\./) // [a, b, c, d]

String#replace


字符串的replace方法,可以将字符串的匹配字符,替换成另外指定的字符

'abc'.replace('a', 'b') // 'bbc'

其第一个参数可以时正则表达式,如果想要全局替换需要添加全局参数 g

'abc'.replace(/[abc]/, 'y') // ybc
'abc'.replace(/[abc]/g, 'y') // yyy 全局替换

在第二个参数中,也可以引用前面匹配的结果

'abc'.replace(/a/, '$&b') // abbc $& 引用前面的匹配字符
'abc'.replace(/(a)b/, '$1a') // aac &n 引用前面匹配字符的分组
'abc'.replace(/b/, '$\'') // aac $` 引用匹配字符前面的字符
'abc'.replace(/b/, "$'") // acc $' 引用匹配字符后面的字符

replace的第二个参数也可以时函数,其第一个参数时匹配内容,后面的参数时匹配的内容

'abc'.replace(/\w/g, function (match, $1, $2) {
    return match + '-'
})
// a-b-c

RegExp


RegExp 是一个全局函数,可以用来创建动态正则,其自身也有一些属性

  • $_
  • $n
  • input
  • length
  • lastMatch
/a(b)/.exec('abc') // ['ab', 'b', index: 0, input: 'abc']

Regexp.$_ // abc 上一次匹配的字符串
RegExp.$1 // b 上一次匹配的捕获分组
RegExp.input // abc 上一次匹配的字符串
RegExp.lastMatch // ab 上一次匹配成功的字符串
RegExp.length // 2 上一次匹配的数组长度

实例属性


正则表达式的实例上也有一些属性

  • flags
  • ignoreCase
  • global
  • multiline
  • source
  • lastIndex
var r = /abc/igm;

r.flags // igm
r.ignoreCase // true 设置或返回一个 Boolean 值,指明模式搜索是否区分大小写。
r.global // true 设置或返回一个 Boolean 值,该值指明在整个搜索字符串时模式是全部匹配还是只匹配第一个
r.multiline // true
r.source // abc

lastIndex比较有意思,表示上次匹配成功的是的索引

var r = /abc/igm;
r.exec('abcabcabc')
r.lastIndex // 3

r.exec('abcabcabc')
r.lastIndex // 6

可以更改lastIndex让其重新开始

var r = /abc/igm;
r.exec('abcabcabc') // ["abc", index: 0]

r.exec('abcabcabc') // ["abc", index: 3]
r.lastIndex = 0
r.exec('abcabcabc') // ["abc", index: 0]

实战例子


常用例子

/(?:0\d{2,3}-)?\d{7}/ // 电话号 010-xxx xxx
/^1[378]\d{9}$/ // 手机号 13xxx 17xxx 18xxx
/^[0-9a-zA-Z_]+@[0-9a-zA-Z]+\.[a-z]+$/ // 邮箱

去除字符串前后空白

str = str.splace(/^\s*|\s*$/g, '')

results matching ""

    No results matching ""