正则表达式 是一个拆分字符串并查询相关信息的过程。

练习网站:JS Bin

正则表达式测试网站:Regular Expression Test Page for JavaScript

正则表达式进修

正则表达式 通常被称为 模式,是一个用简单方式描述或匹配一系列符合某个句法规则的字符串。

创建正则表达式的两种方法:

(1)通过正则表达式字面量(推荐);

var pattern = /test/;

就像字符串是用引号进行界定一样,正则字面量是用 正斜杠 进行界定的。

(2)通过构造 RegExp 对象的实例。

var patter = new RegExp("test");

* 在开发中,如果正则式已知的,则优先选择字面量语法,而构造器方式则是用于在运行时,通过动态构建字符串来构建正则表达式。

正则表达式的3个标志——>这些标志将附加到字面量尾部:/test/ig,或者作为 RegExp 构造器的第二个字符参数:new RegExp("test","ig")

i:不区分大小写

g:匹配模式中的所有实例

m:允许匹配多个行

术语与操作符

精确匹配:如果一个字符不是特殊字符或操作符,则表示该字符必须在表达式中出现。

/test/:匹配字符串 test

匹配一类字符:匹配一个有限字符集中的某一个字符,可以把字符集放到中括号内,来指定该 字符集(类)操作符

若想匹配一组有限字符集以外的字符,可以通过在中括号第一个开括号的后面加一个插入符(^)来实现。

字符集的变异操作:制定一个范围。(-)

[abc]:匹配 "a","b","c" 中的任何一个字符
[^abc]:匹配除了 "a","b","c" 以外的任意字符
[a-m]:匹配 "a" 和 "m" 之间的任何一个小写字母

转义:使用反斜杠对任意字符进行转义,让被转义字符作为字符本身进行匹配。(在字符串中使用时,需要对反斜杠进行转义)

\[:匹配 [ 字符
\\:匹配 \ 字符

匹配开始或结束:插入符号(^)表示从字符串的开头进行匹配,美元符号($)表示该模式必须出现在字符串的结尾

这里的插入符号只是 ^ 字符的一个重载,它还可以用于否定一个字符类集。

/^test$/:同时使用^和$,表明指定的模式必须包含整个候选字符串

重复出现:匹配任意数量的相同字符

  • ?:在一个字符后面加一个问号,定义为该字符是可选的(可以出现一次或根本不出现)

      /t?est/:可以匹配 "test" 和 "est"
  • +:如果一个字符要出现一次或多次,可以使用加号

      /t+est/:可以匹配 "test" "ttest" "tttest"
  • *:如果一个字符要出现0次或多次,可以使用星号

      /t*est/:可以匹配 "test" "ttest" "tttest" "est"
  • {n}:在字符后面的花括号里指定一个数字来表示重复次数

      /a{4}/:表示匹配含有连续四个 "a" 字符的字符串
  • {n,m}:在字符后面的花括号里指定两个数字(用逗号隔开),表示重复次数区间

      /a{4,10}/:表示匹配任何含有连续4个至10个 "a" 字符的字符串
  • {n,}:次数区间的第二个值是可选的(但是要保留逗号),表示一个开区间

      /a{4,}/:表示匹配任何含有连续4个或多于4个 "a" 字符的字符串

这些重复操作符可以是 贪婪的非贪婪的。默认情况下,它们是 贪婪的:它们匹配所有的字符组合。

在操作符后面加一个问号 ? 字符(? 操作符的一个重载),如 a+?,可以让该表达式编程称为 非贪婪的:进行最小限度的匹配。

// 对字符串 "aaa" 进行匹配
/a+/:将匹配所有这三个字符
/a+?/:只匹配一个 a 字符,因为一个 a 字符就可以满足 a+ 术语

预定义字符类:匹配一些不可能用字面量字符来表示的字符(如像回车这样的控制字符、小数位数或一组空白字符)

分组:如果将操作符应用于一组术语,可以像数学表达式一样在该组上使用小括号

/(ab)+/:匹配一个或多个连续出现的子字符串 "ab"
  • 当正则表达式有一部分是用括号进行分组时,它具有双重责任,同时也创建所谓的 捕获

或操作符(OR):可以用竖线 ( | ) 表示或者的关系。

/a|b/:匹配 "a" 或 "b" 字符
/(ab)+|(cd)+/:匹配出现一次或多次的 "ab" 或 "cd"

反向引用:正则表达式中最复杂的术语是,在正则中所定义的 捕获 的反向引用,将 捕获 作为正则表达式中能够成功匹配术语时的候选字符串。

这种术语表示法是在 反斜杠 后面加一个要引用的捕获数量,该数字从1开始,如\1、\2等。

/^([dtn])a\1/:可以任意一个以 "d","t","n" 开头,且后面跟着一个 a 字符,并且再后面跟着的是和第一个捕获相同字符的字符串

不同于 /[dtn]a[dtn]/,a 后面的字符有可能不是 "d" "t" 或 "n" 开头,但这个字符肯定是以触发该匹配的其中一个字符 ("d" "t" 或 "n") 开头。因此,\1 匹配的字符需要在执行的时候才能确定。

编译正则表达式

正则表达式的两个重要阶段:

(1)编译:发生在正则表达式第一次被创建的时候;

(2)执行:发生在我们使用编译过的正则表达式进行字符串匹配的时候。

/*
* 创建编译后正则表达式的两种方式
* 正则表达式在创建之后都处于编译后的状态
*/
var re1 = /test/i; // 通过字面量创建 var re2 = new RegExp("test", "i"); // 通过构造器创建 console.log(re1.toString() == "/test/i"); // true
console.log(re1.test("TesT")); // true
console.log(re2.test("TesT")); // true
console.log(re1.toString() == re2.toString()); // true
console.log(re1 != re2); // true
  • 如果将 re1 的引用再替换成 /test/i 字面量,那么同一个正则表达式可能又被编译多次,所以正则表达式只编译一次,并将其保存在一个变量中以供后续使用,这是一个重要的优化过程

注意:每个正则表达式都有一个独立的对象表示,每次创建正则表达式(也因此被编译),都会为此创建一个新的正则表达式对象。和其他的原始类型不太一样,其结果将永远是独一无二的。

特别重要的一点,用 构造器 创建正则表达式的使用,这种技术允许我们,在运行时通过动态创建的字符串构建和编译一个正则表达式。对于构建大量重用的复杂表达式来说,这是非常有用的。

[例子:]

<div calss="samurai ninja">a</div>
<div class="ninja samurai">b</div>
<div></div>
<span class="samurai ninja ronin">c</span> <script>
function findClassInElements(className, type) { var elems = document.getElementsByTagName(type || "*"); /*
* 正则表达式匹配的字符串:
* 要以空字符串或空格开始,
* 而后跟着指定样式名称,
* 并且紧随其后的是一个空白字符或结束字符串
*/
var regex = new RegExp("(^|\\s)" + className + "(\\s|$)"); var results = []; for (var i = 0, length = elems.length; i < length; i++) {
if (regex.test(elems[i].className)) {
results.push(elems[i]);
}
} return results;
} var results1 = findClassInElements("ninja", "div"); // <div class="ninja samurai">b</div>
var results2 = findClassInElements("ninja", "span"); // <span class="samurai ninja ronin">c</span>
var results3 = findClassInElements("ninja"); // <div class="ninja samurai">b</div>
// <span class="samurai ninja ronin">c</span> console.log(results1.length); // 1
console.log(results2.length); // 1
console.log(results3.length); // 2
</script>
  • 在该例子的正则中,要注意 反斜杠(\\)的使用:\\s。创建带有反斜杠字面量正则表达式时,只需要提供一个反斜杠即可。但是,由于我们在字符串中写反斜杠,所以需要 双反斜杠 进行转义。要明白,我们是用字符串(而不是字面量)来构建正则表达式。

用正则表达式进行捕捉操作

正则表达式的 实用性 表现在捕获已匹配的结果上,这样我们便可以在其中进行处理。

1. 执行简单的捕获 —— match()
  • match() 返回的数组的第一个索引的值总是该匹配的完整结果,然后是每个后续捕获结果。

  • 记住,捕获是由正则表达式中的小括号所定义。

2. 用全局表达式(/g)进行匹配 —— match() exec()
  • 使用 局部正则表达式 时,返回的该数组包含了在匹配操作中成功匹配的整个字符串以及其他捕获结果。

  • 使用 全局正则表达式 时,匹配所有可能的匹配结果,返回的数组包含了全局匹配结果,而不仅仅是第一个匹配结果。

使用 match()exec(),我们总是可以找到想要寻找的精确匹配。

[例子1:]

// 使用 match() 进行全局搜索和局部搜索时的不同
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'"]
console.log(results[0] == "<div class='test'>"); // true
console.log(results[1] == ""); // true
console.log(results[2] == "div"); // true
console.log(results[3] == " class='test'"); // true // 全局正则表达式返回一个数组,匹配所有可能的匹配结果,而不仅仅是第一个匹配结果
var all = html.match(/<(\/?)(\w+)([^>]*?)>/g); // 全局正则匹配 console.log(all); // ["<div class='test'>", "<b>", "</b>", "<i>", "</i>", "</div>"]
console.log(all[0] == "<div class='test'>"); // true
console.log(all[1] == "<b>"); // true
console.log(all[2] == "</b>"); // true
console.log(all[3] == "<i>"); // true
console.log(all[4] == "</i>"); // true
console.log(all[5] == "</div>"); // true

[例子2:]

// 使用 exec() 方法进行捕获和全局搜索
var html = "<div class='test'><b>Hello</b> <i>world!</i></div>"; var tag = /<(\/?)(\w+)([^>]*?)/g, match;
var num = 0; while ((match = tag.exec(html)) !== null) {
console.log(match.length); // 4
num++;
} console.log(num); // 6
3. 捕获的引用

两种方法 ,可以引用捕获到的匹配结果:

(1)自身匹配

(2)替换字符串

与反向引用不同,通过调用 replace() 方法替换字符串获得捕捉的引用时,使用 $1$2$3 语法表示每个捕获的数字

/*
* 使用反向引用匹配 HTML 标签内容
*/
var html = "<div class='hello'>Hello</b> <i>world!</i>"; var pattern = /<(\w+)([^>]*)>(.*?)<\/\1>/g; // 使用捕获的反向引用 var match = pattern.exec(html); // 在测试字符串上进行匹配 console.log(match); // ["<i>world!</i>", "i", "", "world!"] console.log(match[0] == "<div class='hello'>Hello</b>"); // false
console.log(match[1] == "b"); // false
console.log(match[2] == " class='hello'"); // false
console.log(match[3] == "Hello"); // false console.log(match[0] == "<i>world!</i>"); // true
console.log(match[1] == "i"); // true
console.log(match[2] == ""); // true
console.log(match[3] == "world!"); // true /*
* replace() 方法获得捕捉的引用:使用 $1、$2、$3 语法表示每个捕获的数字
*/
"fontFamily".replace(/([A-Z])/g, "-$1").toLowerCase(); // font-family
4. 没有捕获的分组

小括号的双重责任:(1)进行分组;(2)指定捕获。

  • 由于捕获和表达式分组都使用了小括号,所以无法告诉正则表达式处理引擎,哪个小括号是用于分组,以及哪个是用于捕获的。

  • 所以会将小括号既视为分组,又视为捕获,由于有时候需要指定一些正则表达式分组,所以会导致捕获的信息比我们预期的还要多。

要让一组括号不进行结果捕获,正则表达式的语法允许我们在开始括号后加一个 ?: 标记,这就是所谓的 被动子表达式

var pattern = /((?:ninja-)+)sword/;
// 外层小括号 ——> 定义捕获(sword之前的字符串)
// 内层小括号 ——> 针对 + 操作符,对 "ninja-" 文本进行分组
// ?: ——> 只会为外层的括号创建捕获,内层括号被转换为一个被动子表达式

利用函数进行替换

String 对象的 replace 方法:

(1)将正则表达式作为第一个参数,导致在该模式的匹配元素上进行替换。

"ABCDEfg".replace(/[A-Z]/g, "X");	// XXXXXfg,替换所有的大写字母为“X”

(2)(最强大特性)接受一个函数作为替换值,函数的返回值是即将要替换的值。(这样可以在运行时确定该替换的字符串,并掌握大量与匹配有关的信息。)

/*
* 第一个参数:传入完整的字符串
* 第二个参数:捕获结果
*/
function upper(all, letter) {
return letter.toUpperCase();
} /*
* 函数第一次调用时,传入 "-b" 和 "b"
* 函数第二次调用时,传入 "-w" 和 "w"
*/
"border-bottom-width".replace(/-(\w)/g, upper); // borderBottomWidth

当替换值(第二个参数)是一个函数时,每个匹配都会调用该函数并带有一串参数列表。

  • 匹配的完整文本。
  • 匹配的捕获,一个捕获对应一个参数。
  • 匹配字符在源字符串中的索引。
  • 源字符串。

[例子:]

/*
* 让查询字符串转换成另外一个符合我们需求的格式
* 原始字符串:foo=1&foo=2&blah=a&blah=b&foo=3
* 转换成:foo=1,2,3&blah=a,b
*/
function compress(source) {
var keys = {}; // 保存局部键 source.replace(
/([^=&]+)=([^&]*)/g,
function(full, key, value) {
keys[key] = (keys[key] ? keys[key] + "," : "") + value; // 提取键值对信息 /*
* 返回空字符串,因为我们确实不关注源字符串中发生的替换操作
* 我们只需要利用该函数的副作用,而不需要实际替换结果
*/
return "";
}
); // 收集键的信息
var result = [];
for (var key in keys) {
result.push(key + "=" + keys[key]);
} // 使用&将结果进行合并
return result.join("&");
} var origin = "foo=1&foo=2&blah=a&blah=b&foo=3";
compress(origin); // "foo=1,2,3&blah=a,b"
  • 一个有趣点:如何使用字符串的 replace() 方法来遍历一个字符串,而不是一个实际的搜索替换机制。

  • 两个关键点:1)传递一个函数作为替换值参数;2)该函数并不是返回实际的值,而是简单地利用它作为一种搜索手段。

利用这种技巧,我们可以使用 String 对象的 replace() 方法作为字符串搜索机制。搜索结果不仅快速,而且简单、有效。

常用正则表达式

1. 修剪字符串:
// 修剪字符串
function trim(str) {
return (str || "").replace(/^\s+|\s+$/g, "");
} trim(" #id div.class "); // "#id div.class"
2. 匹配换行符:
// 匹配所有的字符,包括换行符
var html = "<b>Hello</b>\n<i>world!</i>"; // 显示换行符没有被匹配到
console.log(/.*/.exec(html)[0] === "<b>Hello</b>"); // true // 使用空白符匹配方式匹配所有的元素,为最佳方案
console.log(/[\S\s]*/.exec(html)[0] === "<b>Hello</b>\n<i>world!</i>"); // true // 用另一种方式匹配所有元素
console.log(/(?:.|\s)*/.exec(html)[0] === "<b>Hello</b>\n<i>world!</i>"); // true
3. Unicode:
// 匹配 Unicode 字符
var text = "\u5FCD\u8005\u30D1\u30EF\u30FC"; var matchAll = /[w\u0080-\uFFFF_-]+/; text.match(matchAll); // ["忍者パワー"]
4. 转义字符:
// 在 CSS 选择器中匹配转义字符
var pattern = /^((\w+)|(\\.))+$/; // 允许匹配一个单词字符,
// 或一个反斜杠及后面跟随任意字符(甚至是另外一个反斜杠)
// 或者两者都可以匹配
var tests = [
"formUpdate", // true
"form\\.update\\.whatever", // true
"form\\:update", // true
"\\f\\o\\r\\m\\u\\p\\d\\a\\t\\e", // true
"form:update" // false,未能匹配到非单词字符(:)
]; for (var n=0; n<tests.length; n++) {
console.log(pattern.test(tests[n]));
}

参考博文:

JavaScript 正则表达式上——基本语法

JavaScript正则表达式下——相关方法

我所认识的JavaScript正则表达式

正则表达式 - 教程

解惑正则表达式中的捕获

《JavaScript Ninja》之正则表达式的更多相关文章

  1. 《JavaScript 闯关记》

    为何写作此课程 stone 主要负责基于 Web 的企业内部管理系统的开发,虽然能够熟练地使用 JavaScript,但随着对 JavaScript 的理解越来越深,才发现自己尚未掌握其精髓. 201 ...

  2. 《JavaScript闯关记》视频版硬广

    <JavaScript闯关记>视频版硬广 stone 在菜航工作时,兼任内部培训讲师,主要负责 JavaScript 基础培训,2016年整理的<JavaScript闯关记>课 ...

  3. 《JavaScript 闯关记》之正则表达式

    由于本课程的核心是 JavaScript,所以本文着重讲解了「正则表达式」在 JavaScript 中的用法,并未深入「正则表达式」的具体细节.如果您尚不了解「正则表达式」,强烈推荐您先学习 正则表达 ...

  4. 《JavaScript 闯关记》之基本包装类型

    为了便于操作基本类型值,JavaScript 还提供了3个特殊的引用类型:Boolean.Number 和 String.实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象 ...

  5. 《JavaScript 闯关记》之对象

    对象是 JavaScript 的数据类型.它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值,因此我们可以把它看成是从字符串到值的映射.对象是动态的,可以随时新增和删除自有属性.对象除了 ...

  6. 《JavaScript 闯关记》之表达式和运算符

    表达式 表达式是由数字.运算符.数字分组符号(如括号).自由变量和约束变量等以能求得数值的有意义排列方法所得的组合.JavaScript 表达式主要有以下几种形式: 原始表达式:常量.变量.保留字. ...

  7. 《JavaScript 闯关记》之语法

    JavaScript 的语法大量借鉴了 C 及其他类 C 语言(如 Java 和 Perl)的语法.因此,熟悉这些语言的开发人员在接受 JavaScript 更加宽松的语法时,一定会有种轻松自在的感觉 ...

  8. JavaScript 闯关记

    DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API.DOM 描绘了一个层次化的节点树,允许开发人员添加.移除和修改页面的某一部分. 节点层次 DOM 可以将任何 HTML 或 XM ...

  9. 《JavaScript 闯关记》之垃圾回收和内存管理

    JavaScript 具有自动垃圾收集机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存.而在 C 和 C++ 之类的语言中,开发人员的一项基本 ...

  10. 《JavaScript 闯关记》之原型及原型链

    原型链是一种机制,指的是 JavaScript 每个对象都有一个内置的 __proto__ 属性指向创建它的构造函数的 prototype(原型)属性.原型链的作用是为了实现对象的继承,要理解原型链, ...

随机推荐

  1. 转载python2进制打包相关

    Python模块——struct(字节流,组包拆包实现) http://www.linuxidc.com/Linux/2014-02/97158.htm [日期:2014-02-24] 来源:Linu ...

  2. webapi方式

    随笔 - 112  文章 - 0  评论 - 334 ASP.NET MVC学习系列(二)-WebAPI请求   继续接着上文 ASP.NET MVC学习系列(一)-WebAPI初探 来看看对于一般前 ...

  3. 使用Chef管理windows集群 | 运维自动化工具

    但凡服务器上了一定规模(百台以上),普通的ssh登录管理的模式就越来越举步维艰.试想Linux发布了一个高危漏洞的补丁,你要把手下成百上千台机器都更新该补丁,如果没有一种自动化方式,那么至少要耗上大半 ...

  4. WEBService动态调用代码

    BasicHttpBinding bind = new BasicHttpBinding(); bind.MaxReceivedMessageSize = int.MaxValue; Endpoint ...

  5. Sticks(poj1011/uva307)

    题目大意: 乔治有一些碎木棒,是通过将一些相等长度的原始木棒折断得到的,给出碎木棒的总数和各自的长度,求最小的可能的原始木棒的长度:(就是将一些正整数分组,每组加起来和相等,使和尽可能小) 一开始做p ...

  6. js unix时间戳转换

    一.unix时间戳转普通时间: var unixtime=1358932051; var unixTimestamp = new Date(unixtime* 1000); commonTime = ...

  7. 3.2 Git 分支 - 分支的新建与合并

    分支的新建与合并 现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会用到这样的工作流程: 开发某个网站. 为实现某个新的需求,创建一个分支. 在这个分支上开展工作. 假设此时,你突然接到一个 ...

  8. CRF++使用小结

    1. 简述 最近要应用CRF模型,进行序列识别.选用了CRF++工具包,具体来说是在VS2008的C#环境下,使用CRF++的windows版本.本文总结一下了解到的和CRF++工具包相关的信息. 参 ...

  9. Spring框架及IOC容器

    Spring是一个非常活跃的开源框架, 它是一个基于IOC和AOP来构架多层JavaEE系统的框架,它的主要目地是简化企业开发.Spring以一种非侵入式的方式来管理你的代码, Spring提倡”最少 ...

  10. K2十年:专注BPM

    <聚·谋·变——K2中国用户大会> 导演:K2中国 主演:K2用户 时长:420分钟 票价:免费 上映日期:2015年7月17日 查看完整视频请关注K2官方微信账号