正则表达式
目录
正则表达式处理各种任务:
- 操作 HTML 节点中的字符串
- 使用 CSS 选择器表达式定位部分选择器
- 判断一个元素是否含有特定的样式名称
- 从 Internet Explorer 的 filter 属性中提取透明度
- ...
正则表达式解释
通常被称为一个模式(pattern),是一个用简单方式描述或者匹配一系列符合某个句法规则的字符串。表达式本身包含了允许定义这些模式的术语和操作符
var pattern = /test/ // 精确匹配字符串 'test'
注意 正则字面量是用正斜杠('/xxx/')进行界定的
或者我们可以构造一个 RegExp 实例,将正则作为字符串传入: var pattern = new RegExp("test")
注意 如果正则是已知的,则优先选择字面量语法,而构造器方式则是用于在运行时,通过动态构建字符串来构建正则表达式
除了表达本身,还有三个标志可以与正则表达式进行关联
- i —— 让正则表达式不区分大小写,所以 /test/i 不仅可以匹配 "test",还可以匹配 "TEST","Test"等
- g —— 匹配模式中的所有实例,而不是默认只匹配第一次出现的结果
- m —— 允许匹配多个行,比如可以匹配文本区元素(textarea)中的值
术语与操作符
精确匹配
var pattern = /test/
匹配一类字符
匹配一个有限字符集中的某一个字符
var pattern = /[abc]/ // 匹配 a,b,c 中的任何一个字符
var pattern1 = /[^abc]/ // 匹配除了 a,b,c 中的任何一个字符
var pattern2 = /[a-m]/ // 匹配 a 到 m 中的之间的所有字符
转义
并不是所有的字符和其他字符字面量都是等价的。像 $ 和 . 这样的特殊字符,表示的是他们自身以外的东西,或者表示为验证术语的操作符。 在正则里,使用反斜杠可以对任意字符进行转义,让被转义字符作为字符本身进行匹配
var pattern = /\[/ // 匹配 [ 字符,而不是匹配表达式的开括号
// 两个反斜杠(\\)则匹配一个反斜杠
匹配开始与匹配结束
// ^:匹配开头
var start = /^test/ // 匹配以 test 开头的字符串
// $:匹配结尾
var start = /test$/ // 匹配以 test 结尾的字符串
重复出现
- 在一个字符后面加一个问号(?),可以定义为该字符是可选的。例如:/t?est/ 可以匹配 "test" 和 "est"
- 如果一个字符要出现一次或多次,可以使用加号(+)。例如:/t+est/ 可以匹配 "test", "ttest", "tttest",但不能匹配 "est"
- 如果一个字符要出现零次或多次,可以使用星号(*)。例如:/t*est/ 可以匹配 "test", "ttest", "tttest", 以及 "est"
- 也可以在字符后面的花括号里指定一个数字来表示重复次数。例如:/a{4}/ 表示匹配含有连续四个 "a" 字符的字符串
- 也可以在字符后面的花括号里指定两个数组(用逗号隔开)来表示重复次数区间。例如:/a{4,,10}/ 表示匹配任何含有连续 4 个至 10 个 "a" 字符的字符串
- 次数区间的第二个值是可选的(但是要保留逗号),其表示一个开区间。例如:/a{4,}/ 表示匹配任何含有连续 4 个或多余 4 个 "a" 字符的字符串
预定义字符类
预定义术语 | 匹配内容 |
---|---|
\t | 水平制表符 |
\b | 空格 |
\v | 垂直制表符 |
\f | 换页符 |
\r | 回车 |
\n | 换行符 |
\cA;\cZ | 控制符,例如:\cM 匹配一个 Control-M |
\x000;\xFFF | 十六进制 Unicode 码 |
\x00;\xFF | 十六进制 ASCII 码 |
. | 匹配除了新行(\n)以外的任意字符 |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字,等价于[^0-9] |
\w | 匹配包括下划线的任意单词字符,等价于[A-Za-z0-9_] |
\W | 匹配任何非单词字符,等价于[^A-Za-z0-9_] |
\s | 匹配任何空白字符,包括空格、制表符、换页符等 |
\S | 匹配任何非空白字符 |
\b | 匹配单词边界 |
\B | 匹配非单词边界 |
分组
如果将操作符应用于一组术语,可以像数学表达式一样在该组上使用小括号。例如:/(ab)+/ 匹配一个活多个连续出现的子字符串 "ab"
或操作符(OR)
可以使用竖线( | )字符表示或者的关系。例如:/a|b/ 匹配 "a" 或 "b" 字符;/(ab)+|(cd)+/ 匹配出现一次或多次的 "ab" 或 "cd"
反向引用
在反斜杠后面加一个要引用的捕获的数量,该数字从 1 开始,如 \1、\2等
// 任意一个以 "d" "t" 或 "n" 开头,且后面跟着一个 "a" 字符,并且再后面跟着的是和第一个捕获相同字符的字符串
var pattern = /[dtn]a\1/
// 其中,\1 匹配的字符需要在执行的时候才能确定
// 匹配 XML 类型标记元素,例如:<strong>what</strong>
var pattern1 = /<(\w)>(.+)<\/\1>/
编译正则表达式
正则表达式的两个重要阶段是编译和执行。 编译发生在正则表达式第一次被创建的时候;执行时发生在我们使用编译过的正则表达式进行字符串匹配的时候
注意:每个正则表达式都有一个独立的对象表示:每次创建正则表达式(被编译),都会为此创建一个新的正则表达式对象
捕获匹配的片段
正则表达式的实用性表现在捕获已匹配的结果上,这样我们便可以在其中进行处理
简单的捕获
String 对象的 match()方法进行局部正则表达式的匹配。match 返回的数组的第一个索引的值总是该匹配的完整结果,然后是每一个后续捕获的结果 注意:捕获是由正则表达式中的小括号所定义
var pattern = /opacity=([^)]+)/
var filter = 'filter:alpha(opacity=50)'
console.log(filter.match(pattern)) // ['opacity=50', '50']
// 第 0 个索引值将是完整的匹配值 opacity=50,与此同时,下一个匹配则是 50
用全局表示式进行匹配
当应用全局表达式(添加一个 g 标记)时,返回值是也是一个数组,但返回的数组包含了全局匹配结果。在这种情况下,每个匹配的捕获结果是不会返回的
var html = "<div class='test'><b>hello</b> <i>world!</i></div>"
var results = html.match(/<(\/?)(\w+)([^>]*?)>/)
console.log(results) // ["<div class='test'>", '', 'div', " class='test'"]
var results1 = html.match(/<(\/?)(\w+)([^>]*?)>/g)
console.log(results) // ["<div class='test'>", '<b>', '</b>', '<i>', '</i>', '</div>']
使用正则表达式的 exec() 方法可以对一个正则表达式进行多次调用,每次调用都可以返回下一个匹配的结果。该方法保存了上一次调用的状态
var html = "<div class='test'><b>hello</b> <i>world!</i></div>"
var tag = /<(\/?)(\w+)([^>]*?)>/g
console.log(tag.exec(html)) // ["<div class='test'>", '', 'div', " class='test'"]
console.log(tag.exec(html)) // ['<b>', '', 'b', '']
console.log(tag.exec(html)) // ['</b>', '/', 'b', '']
console.log(tag.exec(html)) // ['<i>', '', 'i', '']
console.log(tag.exec(html)) // ['</i>', '/', 'i', '']
console.log(tag.exec(html)) // ['</div>', '/', 'div', '']
console.log(tag.exec(html)) // null
捕获的引用
有两种方法可以引用捕获到的匹配结果
- 自身匹配
- 替换字符串: replace()
// 反向引用(\1)
var html = "<div class='test'><b>hello</b> <i>world!</i></div>"
var pattern = /<(\w+)([^>]*)>(.*?)<\/\1>/g
没有捕获的分组
小括号有双重责任:不仅要进行分组操作,还可以指定捕获
// 定义捕获(sword之前的所有字符串)的小括号
// 针对 + 操作符,对 "ninja" 文本进行分组的小括号
var pattern = /((ninja-)+)sword/ // 一切正常,但括号分组的功能,不仅是单一目标捕获了
console.log("ninja-ninja-sword".match(pattern)) // ['ninja-ninja-sword', 'ninja-ninja-', 'ninja-']
// 要想一组括号不进行结果捕获,正则表达式的语法允许我们在开始括号后加一个 ?: 标记(这就是所谓的被动子表达式)
var pattern1 = /((?:ninja-)+)sword/
console.log("ninja-ninja-sword".match(pattern1)) // ['ninja-ninja-sword', 'ninja-ninja-']
// 被动子表达式阻止不必要的捕获('ninja-')
利用函数进行替换
String 对象的 replace() 方法的第一个参数除了可以是 pattern 以外,还可以是一个函数,每个匹配都会调用该函数(全局搜索会在源字符串中匹配所有的模式实例),返回值是即将要替换的值
// 匹配中横线字符后的任意一个字符
function upper(all, letter) {
return letter.toUpperCase()
}
console.log("border-bottom-width".replace(/-(\w)/g, upper)) // borderBottomWidth
利用正则表达式解决常见问题
修剪字符串
// 删除字符串中前后的空格
function trim(str) {
return (str || '').replace(/^\s+|\s+$/g, '')
}
console.log(trim(' test trim ')) // 'test trim'
匹配换行符
点(.)术语:用于匹配除换行符以外的任意字符
// 匹配所有字符,包含换行符
var html = "<b>hello</b>\n<i>world!</i>"
console.log(/.*/.exec(html)) // ['<b>hello</b>'] -> 换行符不会被匹配到
console.log(/[\S\s*]/.exec(html)) // ['<',] -> 匹配所有字符(最佳)
console.log(/(?:.|\s*)/.exec(html)) // ['<'] -> 包含换行符在内的所欲字符
Unicode
// 匹配 Unicode 字符
var text = '\u5FCD\u8005'
var matchAll = /[\w\u0080-\uFFFF_-]+/
console.log(text.match(matchAll)) // ['忍者']
转义字符
编写 CSS 选择器引擎时,要通过转义字符来支持这项功能
总结
- 正则表达式中,有各种术语和操作符,以及在模式匹配的正则表达式中他们之间的结合
- 使用正则表达式的 exec() 方法,以及面向正则的 String 方法,如 match() 和 replace()
- 了解如何利用捕获的片段进行反向引用和替换字符串,以及如何使用被动子表达式避免不必要的捕获
- 了解如何使用函数动态返回替换值,如字符串修剪、匹配像换行符或 Unicode 字符