先来看一个例子

这里有一个例子,用来匹配 URL 的正则表达式。

1
2
3
4
5
6
7
8
var url = 'http://bubuzou.com:80/goodparts?q#fragment'
var parse_url =
/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Z-a-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/
console.log(parse_url.exec(url))
/*
上面这段代码产生的结果如下:
["http://bubuzou.com:80/goodparts?q#fragment", "http", "//", "bubuzou.com", "80", "goodparts", "q", "fragment"]
*/

现在让我们来分解 parse_url 的各个部分,看看它是如何工作的:

  • ^ 字符表示字符串的开始
  • (?:([A-Za-z]+):)?这个因子匹配一个协议名,但是只有当它后面跟随:的时候才匹配。(?:...) 表示一个非捕获型的分组。后缀 ? 表示这个分组是可选的,表示匹配 0 次或者 1 次。
    (...) 表示一个捕获型分组。一个捕获型分组会复制它所匹配的文本,并且将其放到 result 数组里。每个捕获型分组会被指定一个编号。第一个捕获型分组的编号是 1,所以该分组所匹配的结果会出现在 result[1] 中。[...] 表示一个字符类。A-Za-z 这个字符类包含 26 个大写字母和 26 个小写字母。+ 表示这个字符类会被匹配 1 次或多次。: 会按照字面进行匹配。
  • (\/{0,3}) 这个因子是捕获分组 2\/ 表示匹配 / (斜杠),它用 \ 来进行转义。{0,3} 表示会被匹配0-3
  • ([0-9.\-A-Z-a-z]+) 这个因子是捕获分组 3 。它会匹配一个主机名,由一个或多个数字、字母,以及 . 或-组成。
  • (?::(\d+))? 这个因子匹配的是端口号。(\d+) 是捕获分组 4,表示匹配一个或多个数字。
  • (?:\/([^?#]*))? 是一个可选的分组,以一个 / 开始。[^?#] 是捕获分组 5 ,以一个 ^ 开始表示这个类包含除 ?# 之外的所有字符。 * 表示匹配 0 次或多次。
  • (?:\?([^#]*))? 是一个以 ? 开始的分组。包含捕获分组 6,这个分组包含 0 个或多个非 # 的字符
  • (?:#(.*))? 这个可选分组是以 # 开始的,. 会匹配除了行结束符以外的所有字符。
  • $ 表示字符串的结束

所谓正则表达式,就是一种描述字符串结构模式的形式化表达方法。

这是《精通正则表达式》对它的定义。
正则表达式又叫做规则表达式(Regular Expression,简写 regexp).
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式能干嘛

  • 测试字符串是否满足某种模式。比如字符串内是否出现电话号码模式,又称为数据验证。
  • 替换文本。可以用正则表达式来识别字符串中的特定文本,完全删除或者用其他文本替换它。
  • 提取字符串。基本匹配模式从字符串中提取需要的子字符串。
正则表达式已经在很多软件中得到广泛的应用,包括 `*nix(Linux, Unix等)`,`HP` 等操作系统,`PHP`、`C#`、`Java`等开发环境,以及很多的应用软件中,都可以看到正则表达式的影子。

正则表达式结构

优先考虑的方法是使用正则表达式字面量:

1
var patt = /pattern/modifiers;

另一种方法是使用 RegExp 构造器:

1
var patt = new RegExp(pattern, modifiers)
  • pattern(模式) 描述了表达式的模式
  • modifiers(修饰符) 用于指定全局匹配、区分大小写的匹配和多行匹配

修饰符 modifiers:

修饰符 描述
i 执行对大小写不敏感的匹配
g 执行全局匹配
m 执行多行匹配
1
2
3
var patt = /hello/gi
var str = 'Hello world!'
patt.exec(str) // 将会匹配'Hello'

正则表达因子

一个正则表达式因子可以是一个字符、一个圆括号包围的组、一个字符类或者是一个转义序列。下面这些字符都会按照字面进行处理\ / [ ] ( ) { } ? + * | . ^ $如果你希望这些字符按照字面意思去匹配,那需要在其前面加上\进行转义。

1
2
3
var patt = /\.\d+/
var str = '-12.568'
patt.exec(str) // 匹配'.568'

正则表达式特殊字符:

特殊字符 描述
^ 匹配输入字符串开始处的位置;但在中括号表达式中是表示对字符集求反。若要匹配 ^ 字符本身,请使用 \^
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,那么 $ 还匹配 \n\r 前面的位置。若要匹配 $ 字符本身,请使用 \$
() 标记子表达式的开始和结束。可以捕获子表达式以供以后使用。若要匹配这两个字符,请使用 \(\)
* 零次或多次匹配前面的字符或子表达式。若要匹配 * 字符,请使用 \*
+ 一次或多次匹配前面的字符或子表达式。若要匹配 + 字符,请使用 \+
? 零次或一次匹配前面的字符或子表达式,或指示“非贪心”限定符。若要匹配 ? 字符,请使用\?
. 匹配除换行符 \n 之外的任何单个字符。若要匹配 . 请使用 \
[] 标记中括号表达式的开始。若要匹配这些字符,请使用 \[\]
\ 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,字符 n 匹配字符 n\n 匹配换行符。序列 \\ 匹配 \,序列 \( 匹配 (
/ 表示文本正则表达式的开始或结束。若要匹配 / 字符,请使用 \/
{} 标记限定符表达式的开始。若要匹配这些字符,请使用 \{\}
竖线 指出在两个项之间进行选择。要匹配竖线,请使用 \ 竖线。

正则表达式序列

一个正则表达式序列包含一个或多个正则表达式因子。每个因子能选择是否跟随一个量词,这个量词决定了这个因子被允许出现的次数。如果没有量词,则只匹配一次。

1
2
3
var patt = /\d{3}\.\d/
var str = 'abc1893.65d'
patt.exec(str) // 匹配'893.6'

正则表达式分支

一个正则表达式分支包含一个或多个正则表达式序列。这些序列被|字符分隔。如果这些序列中的任何一项符合匹配条件,那么这个选择就被匹配。它会按照书写顺序从左到右依次匹配这些序列。

1
2
3
var str = 'information'
str.match(/or|for/) // 匹配'for'
str.match(/or|orm/) // 匹配'or'

正则表达式分组

正则表达式的分组共有 6 种。

  • (...)捕获型,捕获型分组是一个被包围在圆括号中的正则表达式分支。任何匹配这个分组的字符都会被捕获。每个捕获分组都将有一个编号。第一个捕获分组的编号是 1,以此类推。
1
2
;/(:\d+)/.exec('bubuzou.com:80/')
// 结果是[':80', ':80'],第一个':80'表示整个正则的匹配,第二个':80'表示捕获分组1的匹配
  • (?:...) 非捕获型,仅做简单的匹配,并不会捕获所匹配的文本。也不会进行编号。
1
/(?::\d+)/.exec( 'bubuzou.com:80/' ); 结果是[':80'],不包含分组捕获。
  • (?=pattern) 正向肯定预查。匹配其跟随了 pattern 的字符串。这不是一个好的特性。
1
;/window(?=95|98|2000)/.exec('window98') // 匹配'window'
  • (?!pattern) 正向否定预查。匹配其没有跟随了 pattern 的字符串。这不是一个好的特性。
1
;/window(?!95|98|2000)/.exec('windowNT') // 匹配'window'
  • (?<=pattern) 反向肯定预查。匹配前面带 pattern 的字符串。这不是一个好的特性。
1
;/(?<=95|98|2000)windows/.exec('95windows') // 匹配'windows'
  • (?<!pattern) 反向否定预查。匹配前面没有带 pattern 的字符串。这不是一个好的特性。

RegExp 对象方法

方法 描述
regexp.exec(string) 检索字符串中指定的值。如果匹配成功,则返回一个数组。数组中下标为 0 的值表示匹配的子字符串,下标为 1 的值是分组 1 捕获的文本。如果匹配失败,则会返回 null.
regexp.test(string) 检索字符串中指定的值。返回 truefalsetest 方法中不需要对 regexp 进行全局匹配的配置,因为结果都一样。
string.match(regexp) 获取匹配结果。如果没有 g 标识,则结果和 regexp.exec(string) 一样。如果有 g 标识,则返回包含所有匹配结果(除了分组捕获之外)的数组。
string.replace(searchValue, replaceValue) replace 方法对字符串执行查找和替换工作,并返回一个新的字符串。参数 searchValue 可以是一个字符串或者正则表达式。如果是字符串,则在字符串中第一次出现的地方被替换。如果是正则表达式且带有 g,则会替换所有匹配,如果没有 g 则只会替换第一个匹配。replaceValue 可以是一个字符串或者一个函数。如果是一个函数则有特别含义。如果是一个函数,则每遇到一个匹配函数就会被调用一次,而函数返回的字符串将作为替换的文本。
string.search(regexp) indexOf 方法类似,只是它接受的参数是一个正则表达式而非字符串。如果找到匹配,则返回第一个匹配的首字符的位置,没找到就返回 -1
string.split(separator, limit) string 分割成片段来创建一个字符串数组。limit 可以限制分割的数量。separator可以是字符串或正则表达式。

regexp.exec(string):

1
2
3
4
5
6
7
8
9
// RegExpObject.exec(string);
var str = 'user1abcuser2abc',
patt = /user\d/g,
result
while ((result = patt.exec(str))) {
console.log(result)
}
// 第一次:["user1", index: 0, input: "user1abcuser2abc"]
// 第二次:["user2", index: 8, input: "user1abcuser2abc"]
如果regexp带有g标识,那么查找不是从这个字符串的起始位置开始,而是从regexp.lastIndex位置开始。如果匹配成功,那么regexp.lastIndex将被设置为该匹配后第一个字符的位置。不成功的匹配会将regexp重新设置为0。

regexp.test(string):

1
;/Hello/i.test('hello world') // true

string.match(regexp):

1
2
'user1abcuser2abc'.match(/user\d/g) // ['user1', 'user2']
'user1abcuser2abc'.match(/user\d/) // ['user1']

string.replace(searchValue, replaceValue):

1
2
3
var patt = /\((\d{3})\)/g,
str = '(555)666-1212'.replace(patt, '$1-')
console.log(str) // 结果:'555-666-1212'
美元符号序列 替换对象
$$ $
$& 整个匹配的文本
$number 分组匹配的文本
$ 匹配之前的文本
$ 匹配之后的文本

string.search(regexp):

1
'hello world'.search(/world/) // 6

string.split(separator, limit):

1
2
'123456'.split('', 3) // ["1", "2", "3"]
'last, first, middle'.split(/\s*,\s*/) // ['last', 'first', 'middle']

正则表达式字符表

量词 描述
* 零次或多次匹配前面的字符或子表达式。例如,zo 匹配 zzoo。 等效于 {0,}
+ 一次或多次匹配前面的字符或子表达式。例如,zo+ 匹配 zozoo,但不匹配 z+ 等效于 {1,}
? 零次或一次匹配前面的字符或子表达式。例如,do(es)? 匹配 dodoes 中的 do? 等效于 {0,1}
{n} n 是非负整数。正好匹配 n 次。例如,o{2} 不匹配 Bob 中的 o,但匹配 food 中的两个 o
{n,} n 是非负整数。至少匹配 n 次。例如,o{2,} 不匹配 Bob 中的 o,而匹配 foooood 中的所有 oo{1,} 等效于 o+o{0,} 等效于 o*
{n,m} mn 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,o{1,3} 匹配 fooooood 中的头三个 oo{0,1} 等效于 o?。注意:您不能将空格插入逗号和数字之间。

方括号:方括号用于查找某个范围内的字符

表达式 描述
[abc] 查找方括号之间的任何字符
[^abc] 查找任何不在方括号之间的字符
[0-9] 查找任何从 09 的数字
[a-z] 查找任何从小写 a 至小写 a 的字符
() 匹配一个子表达式的开始和结束位置

元字符:拥有特殊含义的字符

元字符 描述
. 查找单个字符,除了换行和行结束符
\w 查找单词字符
\W 查找非单次字符
\b 匹配一个字的边界,即字与空格间的位置
\B 非字符边界匹配
\d 查找数字
\D 查找非数字字符
\s 查找空白字符
\S 查找非空白字符