JavaScript 正则表达式

正则表达式语法

1
/pattern/modifiers

正则定义方法

  1. var reg = new Regexp(/[a-z]/)
  2. var reg = /[a-z]/

正则表达式修饰符

修饰符描述
i对大小写不敏感
g全局匹配
m多行匹配
u处理四字解的UTF-16编码
yy修饰符确保匹配必须从剩余的第一个位置开始

示例

i,g最常用的,不解释了

1
2
3
4
5
var reg = /abc/i
console.log(reg.test('aBc')) // true

var reg = /abc/g
console.log('abcedfabc') // ['abc', 'abc']

m — 当目标字符串存在换行符\n或\r,而且表达式中含有^或$, m才有作用

1
2
3
4
5
6
// ^ $ 换行后是句首或句尾也能生效
var str = 'ef\r\nabcd'
var reg1 = /^abc/m
var reg2 = /^abc/
console.log(reg1.test(str)) // true
console.log(reg2.test(str)) // false

u — \uD83D\uDC2A是一个四个字节的UTF-16编码,代表一个字符。

1
2
3
4
5
/^\uD83D/u.test('\uD83D\uDC2A')
// false

/^\uD83D/.test('\uD83D\uDC2A')
// true

y — 每次从剩余字符的第一位开始匹配

1
2
3
4
5
var s = 'aaa_aa_a';
var r = /a+_/y;

r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"]

使用 lastIndex属性可以更好地说明y修饰符,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const REGEX = /a/g;

// 指定从2号位置(y)开始匹配
REGEX.lastIndex = 2;

// 匹配成功
const match = REGEX.exec('xaya');

// 在3号位置匹配成功
match.index // 3

// 下一次匹配从4号位开始
REGEX.lastIndex // 4

// 4号位开始匹配失败
REGEX.exec('xaxa') // null

上面代码中,lastIndex属性指定每次搜索的开始位置,g修饰符从这个位置开始向后搜索,直到发现匹配为止。

y修饰符同样遵守lastIndex属性,但是要求必须在lastIndex指定的位置发现匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const REGEX = /a/y;

// 指定从2号位置开始匹配
REGEX.lastIndex = 2;

// 不是粘连,匹配失败
REGEX.exec('xaya') // null

// 指定从3号位置开始匹配
REGEX.lastIndex = 3;

// 3号位置是粘连,匹配成功
const match = REGEX.exec('xaxa');
match.index // 3
REGEX.lastIndex // 4

进一步说,y修饰符号隐含了头部匹配的标志^

s — 使得.可以匹配任意单个字符

1
2
/foo.bar/.test('foo\nbar') // false
/foo.bar/s.test('foo\nbar') // true

正则属性

sticky属性

表示是否设置了y修饰符;

1
2
var r = /hello\d/y;
r.sticky // true

flags属性

返回正则表达式的修饰符

1
2
/abc/ig.flags
'gi'

source属性

返回正则表达式的正文

1
2
/abc/ig.source
// "abc"

dotAll属性

点匹配非换行符的一切字符,加s包含换行符

1
2
const re = /foo.bar/s;
re.dotAll // true

正则表达式模式

表达式

表达式描述
[abc]查找方括号之间的任何字符
[^abc]匹配除了方括号内的所有字符
[0-9]查找任何从0至9的数字
(x|y)查找任何以|分隔的选项
[\s\S]匹配所有,\s匹配所有空白符(包括换行),\S匹配所有非空白字符(包括换行)
\w匹配数字、字母、下划线(等同于[A-Za-z0-9_])

元字符

元字符描述
\d查找数字
\s查找空白字符
\b匹配单词边界(单位指的是\w)
\uxxxx查找以十六进制数 xxxx 规定的 Unicode 字符

特殊字符

特殊字符描述
()标记一个子表达式的开始位置和结束位置
*匹配零次或多次
+匹配一次或多次
.匹配除换行符\n之外的任何字符
[标记一个中括号表达式的开始
匹配零次或一次
\用于转义
{标记限定表达式的开始
|表示两项之间的选择

限定符

字符描述
{n}n 是一个非负整数。匹配确定的 n 次
{n,}n 是一个非负整数。至少匹配n 次
{n,m}m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。

定位符

字符描述
$匹配输入字符串结尾位置。
^匹配输入字符串的开始位置
\b匹配一个单词边界,即字与空格间的位置。
\B非单词边界匹配。

正则常用方法

Test

test方法用于检测一个字符串是否匹配某个模式。是返回true,否返回false。

Match

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配,没有则为null

返回值:存放匹配结果的数组。该数组内容依赖于regexp是否具有全局标志g。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 没有g
'[1200::2001]'.match(/^\[(.*)\]$/)

// 返回一个stringObject
{
0: "[1200:2001]",
1: "1200:2001",
index: 0, // index 属性声明的是匹配文本的起始字符在 stringObject 中的位置
iput: "[1200:2001]"
}

// 有g
["[1200::2001]"] // input 属性声明的是对 stringObject 的引用

Exec

exec()方法用于检索字符传中的正则表达式的匹配,返回一个数组,其中存放匹配到的结果,没有则为null

exec的用法跟match大体类似,不同之处在于lastIndex的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 没有g
/^\[(.*)\]$/.exec('[1200::2001]')

// 返回一个stringObject
{
0: "[1200:2001]",
1: "1200:2001",
index: 0, // index 属性声明的是匹配文本的起始字符在 stringObject 中的位置
iput: "[1200:2001]"
}

// 有g
["[1200::2001]"] // input 属性声明的是对 stringObject 的引用

当 RegExpObject 是一个全局正则表达式时, exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。

可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

示例:

  • 不带全局标志g

exec

  • 带有全局标志g

exec_g

Replace

replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

返回值: 一个新的字符串,是用 replacement 替换了 regexp 的第一次匹配或所有匹配之后得到的。

1
stringObject.replace(regexp/substr,replacement)

当replacement为函数时,每个匹配都调用该函数,它返回的字符串作为替换文本使用。

replacement中的特殊字符:

正则中小括号匹配子表达式,也就是$1-$99

字符替换文本
$1、$2、…、$99与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。
$&与 regexp 相匹配的子串。
$`位于匹配子串左侧的文本。
$’位于匹配子串右侧的文本。
$$直接量符号。
  • 基本用法
1
2
3
4
5
var str = "hello world!";
console.log(str.replace("l","L")); // heLlo world!

var str = "hello world!";
console.log(str.replace(/l/g,"L")); // heLLo worLd!
  • 替换模式中的子表达式

    • $1-$99
    1
    2
    3
    var str = "javascript is the best programming language in the world";
    reg = /(\S+)(\s+)(\S+)/g;
    console.log(ss.replace(re, "$3$2$1")); // is javascript best the language programming the in world。
    • $&
    1
    2
    3
    var str = "javascript is the best programming language in the world";
    reg = /javascript/g
    console.log(str.replace(reg,"$& and c++")) // javascript and c++ is the best programming language in the world
    • $`
    1
    2
    3
    var str = "javascript is the best programming language in the world";
    reg = /script/g
    console.log(str.replace(reg,"$`")) // javajava is the best programming language in the world
    • $’
    1
    2
    3
    var str = "javascript is the best programming language in the world";
    reg = /the/g
    console.log(str.replace(reg,"$'-earth")) // javascript is best programming language in the world-earth best programming language in world-earth world
    • $$
    1
    2
    3
    var str = "javascript is the best programming language in the world";
    reg = /the/g
    console.log(str.replace(reg,"$$")) // javascript is $ best programming language in $ world
  • replacement为函数

参数:

  1. 正则表达式匹配到的字符串

  2. 子表达式匹配到的字符串

    ···

  3. 匹配到表达式的索引

  4. 字符串本身

replace

零宽断言

  • 什么是零宽断言?

零宽断言正如它的名字,是一种零宽度的匹配,它匹配到的内容不会保存到匹配结果中去,最终匹配结果指示一个位置而已。

简单理解就是在本身匹配结果中再加一层筛选。

  • 何时使用零宽断言?

在使用正则表达式时,有时我们需要捕获的内容前后必须是特定内容,但又不捕获这些特定内容的时候,零宽断言就起作用了。

  • 如何使用零宽断言?

作用是给指定位置添加一个限定条件,用来规定此位置之前或者之后的字符必须满足限定条件才能使正则中的表达式匹配成功。

  • 相关规则

    • ?:pattern 匹配pattern但不获取匹配结果
    1
    2
    3
    /(a+)(?:b+)/ //匹配结果为a且后面跟着的是b

    'aaabbb,'.match(/(a+)(?:b+)/g) // aaabbb
    • ? = pattern 匹配内容右侧必须为pattern
    1
    2
    3
    /ab(?=[A-Z])/ //匹配结果为ab且后面跟着的是大写字母的

    'abcabCab,'.match(/ab(?=[A-Z])/) // 匹配到index为3的stringObject
    • ?!pattern 匹配右侧不是pattern内容
    1
    2
    3
    4
    /ab(?![A-Z])/ //匹配结果为ab且后面跟着的不是大写字母的

    'abcabCab,'.match(/ab(?=[A-Z])/) // 匹配到index为0的stringObject
    'abcabCab,'.match(/ab(?=[A-Z])/g) // ['ab', 'ab']
    • ?<=pattern 这个位置之前为pattern的内容
    1
    2
    3
    /(?<=[A-Z])ab/ //匹配结果为ab且前面是大写字母的

    'abcabCab,'.match(/(?<=[A-Z])ab/) // 匹配到index为6的stringObject
    • ?<!pattern 匹配这个位置之前不为pattern的内容
    1
    2
    3
    4
    5
    6
    /(?<![A-Z])ab/ //匹配结果为ab且前面不是大写字母的

    'abcabCab,'.match(/ab(?=[A-Z])/) // 匹配到index为0的stringObject
    'abcabCab,'.match(/ab(?=[A-Z])/g) // ['ab', 'ab']

    'abcEFghIjk,'.match(/(?<![A-Z])[a-z]+/g)  // ["abc", "h", "k"]
  • 补充案例 :

    • 千分符
    1
    2
    3
    var str = "123456789.12"

    str.replace(/(\d)(?=(\d{3})+(\.|$))/g, "$1,") // "123,456,789.12"
    • 密码强度

      1. 至少包含一位大写字母

        即从开头到结尾不能全由小写字母和数字组成;说明一定包含大写字母或特殊符号;即/^(?![a-z]+$)/

      2. 至少包含一位数字

        即开头到结尾不能全由字母组成;说明一定包含数字或特殊符号;即/^(?![a-zA-Z]+$)/

      3. 六位以上字母数字的组合,不能有特殊符号

        即排除特殊符号;即 /^[a-zA-Z0-9]{6,}$/

    1
    2
    3
    // 组合可得正则 /^(?![a-z]+$)(?![a-zA-Z]+$)[a-zA-Z0-9]{6,}$/

    /^(?![a-z]+$)(?![a-zA-Z]+$)[a-zA-Z0-9]{6,}$/.test('passWord1') // true