先来看一个例子
这里有一个例子,用来匹配 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))
|
现在让我们来分解 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)
|
正则表达因子
一个正则表达式因子可以是一个字符、一个圆括号包围的组、一个字符类或者是一个转义序列。下面这些字符都会按照字面进行处理\ / [ ] ( ) { } ? + * | . ^
$如果你希望这些字符按照字面意思去匹配,那需要在其前面加上\进行转义。
1 2 3
| var patt = /\.\d+/ var str = '-12.568' patt.exec(str)
|
正则表达式特殊字符:
特殊字符 |
描述 |
^ |
匹配输入字符串开始处的位置;但在中括号表达式中是表示对字符集求反。若要匹配 ^ 字符本身,请使用 \^ 。 |
$ |
匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,那么 $ 还匹配 \n 或 \r 前面的位置。若要匹配 $ 字符本身,请使用 \$ 。 |
() |
标记子表达式的开始和结束。可以捕获子表达式以供以后使用。若要匹配这两个字符,请使用 \( 和 \) 。 |
* |
零次或多次匹配前面的字符或子表达式。若要匹配 * 字符,请使用 \* 。 |
+ |
一次或多次匹配前面的字符或子表达式。若要匹配 + 字符,请使用 \+ 。 |
? |
零次或一次匹配前面的字符或子表达式,或指示“非贪心”限定符。若要匹配 ? 字符,请使用\? 。 |
. |
匹配除换行符 \n 之外的任何单个字符。若要匹配 . 请使用 \ 。 |
[] |
标记中括号表达式的开始。若要匹配这些字符,请使用 \[ 和\] 。 |
\ |
将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,字符 n 匹配字符 n 。\n 匹配换行符。序列 \\ 匹配 \ ,序列 \( 匹配 ( 。 |
/ |
表示文本正则表达式的开始或结束。若要匹配 / 字符,请使用 \/ 。 |
{} |
标记限定符表达式的开始。若要匹配这些字符,请使用 \{ 和 \} 。 |
竖线 |
指出在两个项之间进行选择。要匹配竖线,请使用 \ 竖线。 |
正则表达式序列
一个正则表达式序列包含一个或多个正则表达式因子。每个因子能选择是否跟随一个量词,这个量词决定了这个因子被允许出现的次数。如果没有量词,则只匹配一次。
1 2 3
| var patt = /\d{3}\.\d/ var str = 'abc1893.65d' patt.exec(str)
|
正则表达式分支
一个正则表达式分支包含一个或多个正则表达式序列。这些序列被|字符分隔。如果这些序列中的任何一项符合匹配条件,那么这个选择就被匹配。它会按照书写顺序从左到右依次匹配这些序列。
1 2 3
| var str = 'information' str.match(/or|for/) str.match(/or|orm/)
|
正则表达式分组
正则表达式的分组共有 6 种。
(...)
捕获型,捕获型分组是一个被包围在圆括号中的正则表达式分支。任何匹配这个分组的字符都会被捕获。每个捕获分组都将有一个编号。第一个捕获分组的编号是 1
,以此类推。
1 2
| ;/(:\d+)/.exec('bubuzou.com:80/')
|
(?:...)
非捕获型,仅做简单的匹配,并不会捕获所匹配的文本。也不会进行编号。
1
| /(?::\d+)/.exec( 'bubuzou.com:80/' ); 结果是[':80'],不包含分组捕获。
|
(?=pattern)
正向肯定预查。匹配其跟随了 pattern
的字符串。这不是一个好的特性。
1
| ;/window(?=95|98|2000)/.exec('window98')
|
(?!pattern)
正向否定预查。匹配其没有跟随了 pattern
的字符串。这不是一个好的特性。
1
| ;/window(?!95|98|2000)/.exec('windowNT')
|
(?<=pattern)
反向肯定预查。匹配前面带 pattern
的字符串。这不是一个好的特性。
1
| ;/(?<=95|98|2000)windows/.exec('95windows')
|
(?<!pattern)
反向否定预查。匹配前面没有带 pattern
的字符串。这不是一个好的特性。
RegExp 对象方法
方法 |
描述 |
regexp.exec(string) |
检索字符串中指定的值。如果匹配成功,则返回一个数组。数组中下标为 0 的值表示匹配的子字符串,下标为 1 的值是分组 1 捕获的文本。如果匹配失败,则会返回 null . |
regexp.test(string) |
检索字符串中指定的值。返回 true 或 false 。 test 方法中不需要对 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
| var str = 'user1abcuser2abc', patt = /user\d/g, result while ((result = patt.exec(str))) { console.log(result) }
|
如果regexp带有g标识,那么查找不是从这个字符串的起始位置开始,而是从regexp.lastIndex位置开始。如果匹配成功,那么regexp.lastIndex将被设置为该匹配后第一个字符的位置。不成功的匹配会将regexp重新设置为0。
regexp.test(string):
1
| ;/Hello/i.test('hello world')
|
string.match(regexp):
1 2
| 'user1abcuser2abc'.match(/user\d/g) 'user1abcuser2abc'.match(/user\d/)
|
string.replace(searchValue, replaceValue):
1 2 3
| var patt = /\((\d{3})\)/g, str = '(555)666-1212'.replace(patt, '$1-') console.log(str)
|
美元符号序列 |
替换对象 |
$$ |
$ |
$& |
整个匹配的文本 |
$number |
分组匹配的文本 |
$ |
匹配之前的文本 |
$ |
匹配之后的文本 |
string.search(regexp):
1
| 'hello world'.search(/world/)
|
string.split(separator, limit):
1 2
| '123456'.split('', 3) 'last, first, middle'.split(/\s*,\s*/)
|
正则表达式字符表
量词 |
描述 |
* |
零次或多次匹配前面的字符或子表达式。例如,zo 匹配 z 和 zoo 。 等效于 {0,} 。 |
+ |
一次或多次匹配前面的字符或子表达式。例如,zo+ 匹配 zo 和 zoo ,但不匹配 z 。+ 等效于 {1,} 。 |
? |
零次或一次匹配前面的字符或子表达式。例如,do(es)? 匹配 do 或 does 中的 do 。? 等效于 {0,1} 。 |
{n} |
n 是非负整数。正好匹配 n 次。例如,o{2} 不匹配 Bob 中的 o ,但匹配 food 中的两个 o 。 |
{n,} |
n 是非负整数。至少匹配 n 次。例如,o{2,} 不匹配 Bob 中的 o ,而匹配 foooood 中的所有 o 。o{1,} 等效于 o+ 。o{0,} 等效于 o* 。 |
{n,m} |
m 和 n 是非负整数,其中 n <= m 。匹配至少 n 次,至多 m 次。例如,o{1,3} 匹配 fooooood 中的头三个 o 。o{0,1} 等效于 o? 。注意:您不能将空格插入逗号和数字之间。 |
方括号:方括号用于查找某个范围内的字符
表达式 |
描述 |
[abc] |
查找方括号之间的任何字符 |
[^abc] |
查找任何不在方括号之间的字符 |
[0-9] |
查找任何从 0 至 9 的数字 |
[a-z] |
查找任何从小写 a 至小写 a 的字符 |
() |
匹配一个子表达式的开始和结束位置 |
元字符:拥有特殊含义的字符
元字符 |
描述 |
. |
查找单个字符,除了换行和行结束符 |
\w |
查找单词字符 |
\W |
查找非单次字符 |
\b |
匹配一个字的边界,即字与空格间的位置 |
\B |
非字符边界匹配 |
\d |
查找数字 |
\D |
查找非数字字符 |
\s |
查找空白字符 |
\S |
查找非空白字符 |