先来看一个例子
这里有一个例子,用来匹配 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 |
查找非空白字符 |