JavaScript语言精粹-笔记5-正则表达式
正则表达式(Regular Expressions)的定义
JavaScript的许多特性都借鉴自其他语言. 语法借鉴自java, 函数借鉴自Scheme, 原型继承借鉴自Self, 正则表达式借鉴自Perl.
正则表达式是一门简单语言的语法规范. 它应用在一些方法中, 对字符串中的信息实现查找、替换和提取操作. 可处理正则表达式的方法有regexp.exec
、regexp.test
、string.match
、string.replace
、string.search
和string.split
.
在JavaScript中正则表达式相较于等效的字符串处理有着显著的性能优势.
正则表达式起源于对形式语言(formal language)的数学研究, Ken Thompson基于Stephen Kleene对type-3语言的理论研究写出了一个切实可用的模式匹配器, 它能够被嵌入到编程语言和像文本编辑器这样的工具中.
一个例子(An Example)
解析URL的例子
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
var url = "http://wwww.ora.com:80/goodparts?q#fragment";
var result = parse_url.exec(url);
var names = ['url', 'scheme', 'slash', 'host', 'prot', 'path', 'query', 'hash'];
var blanks = ' ';
for (var i = 0; i < names.length; i ++) {
console.log(names[i] + ':' + blanks.substring(names[i].length), result[i]);
}
/* 输出结果如下
url: http://wwww.ora.com:80/goodparts?q#fragment
scheme: http
slash: //
host: wwww.ora.com
prot: 80
path: goodparts
query: q
hash: fragment
*/
分解/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/
分析:
^
: 表示匹配字符串的开始(?:([A-Za-z]+):)?
: 用于匹配协议名
(?:...)
表示一个非捕获型分组(noncapturing group);(([A-Za-z]+):)
表示A到Z和a到z的字母匹配一次或多次,+
表示一次或多次, 后面的:
表示协议名后必须接一个:
- 末尾
?
表示前面的匹配零次或一次, 即前面匹配是可选的
(\/{0,3})
: 用于匹配0至3个/
,\/
表示将/
转义([0-9.\-A-Za-z]+)
: 用于匹配host, 表示字符0至9、A至Z、a至z、.
、-
匹配一次或多次(?::(\d+))?
: 用于匹配端口, 端口前面必须有字符:
, 端口匹配是可选的(?:\/([^?#]*))?
: 用于匹配可选分组path, 匹配到的字符串以/
开头,[^?#]
表示匹配除?
和#
的字符,*
表示匹配任意次数(?:\?([^#]*))?
: 用于匹配可选分组query, 匹配到的字符串以?
开头, 匹配除#
字符串的字符任意次数(?:#(.*))?
: 用于匹配可选分组hash, 匹配到的字符串以#
开头, 后接任意字符$
: 表示字符串结束后面不会再有其它字符
判断是否合法数字的例子
一个合法数字可能由一个整数部分加上一个可选的-(负)
号、一个可选的小数部分和一个可选的指数部分组成.
var parse_number = /^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i;
var test = function (num) {
console.log(parse_number.test(num));
}
test('1'); // true
test('number'); // false
test('98.6'); // true
test('132.21.86.100'); // false
test('123.45E-67'); // true
test('123.45D-67'); // false
分解/^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i
分析:
/^ $/i
: 表示匹配的是一个独立的字符串, 且用i
指定不区分大小写-?
: 可选匹配-
号\d+
: 表示匹配一个或多个数字组成的整数部分(?:\.\d*)?
: 表示匹配可选的小数部分, 小数部分需以.
开始后接一个或多个数字(?:e[+\-]?\d+)?
: 表示匹配可选的指数部分, 指数部分以e或者E(不区分大小写)开头, 后接可选的+
或-
, 再以一个或多个数字结尾
结构(Construction)
创建一个RegExp对象的方式有两种, 一种是字面量的形式, 另一种是用RegExp构造器生成.
字面量方式
正则表达式的字面量被包围在一堆斜杠(/
)中, 后接g
、i
和m
标识符, 如: var my_regexp = /"(?:\\.|[^\\\"])*"/g;
正则表达式标识符及其含义:
标识 | 含义 |
---|---|
g | 全局的(匹配多次) |
i | 大小写不敏感(忽略字符大小写) |
m | 多行(^和$能匹配行结束符) |
RegExp构造器方式
RegExp构造器接收一个字符串, 并将它编译成一个RegExp对象, 第二个参数传入标识符, 但需要注意的是斜杠\
需要双转义, 引号也需要转义, 如: var my_regexp = new RegExp("\"(?:\\\\.|[^\\\\\\\"])*\"", 'g')
, 可对比字面量创建方式代码
实例化的RegExp对象包含的属性:
属性 | 用法 |
---|---|
global | 如果标识g被使用则值为true |
ignoreCase | 如果标识i被使用则值为true |
multiline | 如果标识m被使用则值为true |
lastIndex | 下一次exec匹配开始的索引, 初始值为0 |
source | 正则表达式源码文本 |
元素(Elements)
正则表达式分支(Regexp Choice)
一个正则表达式分支包含一个或多个正则表达式序列. 这些序列被|(竖线)
字符分隔, 如果这些序列中的任何一项符合匹配条件, 则这个选择被匹配, 它会按顺序依次匹配这些序列项.
如"into".match(/in|int/)
, 会在into
中匹配in
, 但不会匹配int
, 因为in
已被成功匹配.
正则表达式序列(Regexp Sequence)
一个正则表达式序列包含一个或多个正则表达式因子. 每个因子能选择是否跟随一个量词, 这个量词决定着这个因子被允许出现的次数. 如果没有指定这个量词, 那么该因子只会被匹配一次
如表达式/\d+/
(\d
是因子, +
是量词)或表达式/(?:\.\d)?/
((?:\.\d)
是因子, ?
是量词).
正则表达式因子(Regexp Factor)
一个正则表达式因子可以是一个字符、一个由圆括号包围的组、一个字符类或者是一个转义序列.
除了控制字符和特殊字符, 其它所有的字符都会被按照字面处理, 控制字符和特殊字符包括:
\ / [ ] () { } ? + * | . ^ $
注意:
- 需要以字面的方式去匹配以上字符需要在字符前面加
\
\
可以使除字母和数字外的其它字符字面化, 所以对字母和数字使用无效- 一个未被转义的
.
会匹配除行结束符以外的任何字符 - 当lastIndex属性值为0时, 一个未转义的^会匹配文本的开始. 当指定了m标识时, 它也能匹配行结束符
- 一个未转义的
$
将匹配文本的结束. 当指定了m标识时, 它也能匹配行结束符
正则表达式转义(Regexp Escape)
反斜杠字符在正则表达式因子中与其它字符串中一样均表示转义:
\d
: 等同于[0-9]
, 表示匹配一个数字\D
: 等同于[^0-9]
, 与\d
功能相反\s
: 等同于[\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]
, 表示匹配一个空白符\S
: 等同于[^\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]
, 与\s
功能相反\w
: 等同于[0-9A-Za-z]
, 设计的本意是希望表示出现在话语中的字符, 但由于Unicode的出现, 文本往往不止这些字符, 所以作用不大\W
: 等同于[^0-9A-Za-z]
, 与\w
功能相反\b
: 用于对文本的字边界(word-boundary)标识的匹配, 因为使用\w
去实现的所以基本没用\数字
如\1
、\2
等: 用于指向对应分组所捕获的文本的一个引用, 以至达到重复匹配的作用, 如匹配重复单词:var doubled_word = /([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1/gi
正则表达式分组(Regexp Group)
分为4种:
- 捕获型: 是一个被包围在圆括号中的正则表达式分支, 任何匹配这个分组的字符都会被捕获, 每个捕获型分组都被指定了一个数字, 数字序列从1开始计数
- 非捕获型: 与捕获型不同的是圆括号内用
?:
前缀标识, 非捕获型并不会捕获文本, 也不会干扰捕获型分组的编号, 相比较捕获型会带来微弱的性能优势 - 向前正向匹配(Positive lookahead): 在圆括号中以
?=
前缀标识, 它类似与非捕获型分组, 但在这个组匹配后, 文本会倒回到它开始的地方, 实际上并不匹配任何东西, 这不是个好特性 - 向前负向匹配(Negative lookahead): 在圆括号中以
?!
前缀标识, 它类似于向前正向匹配分组, 但只有当它匹配失败时它才继续向前进行匹配, 这不是一个好特性
正则表达式字符集(Regexp Class)
正则表达式字符集是一种指定一组字符的便利方式, 如[aeiou]
表示(?:a|e|i|o|u)
, [a-z]
表示匹配一个所有小写字符(?:a|b|c|...其它小写字母...|y|z)
对32个特殊字符分组及字符集表示:
/*
特殊字符
! " # $ % & ' ( ) * + , - . /
字符集表示
[!-\/]
*/
/*
特殊字符
: ; < = > ? @
字符集表示
[:-@]
*/
/*
特殊字符
[ \ ] ^ _ `
字符集表示
[\[-`]
*/
/*
特殊字符
{ | } ~
字符集表示
[{-~]
*/
在方括号内首部加^
表示字符集求反
正则表达式量词(Regexp Quantifier)
正则表达式因子可以用一个正则表达式量词后缀来决定这个因子应该被匹配的次数. 量词使用大括号包裹数字的形式表示, 如:
{3}
表示前面因子匹配三次{3,6}
表示前面因子会匹配3、4、5、6次{3,}
表示前面因子会匹配3次或更多次?
等同于{0,1}
*
等同于{0,}
+
等同于{1,}
如果只有一个量词, 表示趋向于进行贪婪性匹配, 即匹配尽可能多的副本直至达到上限. 如果这个量词附加一个后缀?
, 则表示趋向于进行非贪婪匹配, 即只匹配必要的副本就好. 一般情况下最好坚持使用贪婪性匹配.