第五章 正则表达式的拆分

5.1. 结构和操作符

编程语言一般都有操作符。只要有操作符,就会出现一个问题。

当一大堆操作在一起时,先操作谁,又后操 作谁呢?

为了不产生歧义,就需要语言本身定义好操作顺序,即所谓的优先级。

上面操作符的优先级从上至下,由高到低。

5.2. 注意要点

关于结构和操作符,还是有几点需要强调:

5.2.1 匹配字符串整体问题

因为是要匹配整个字符串,我们经常会在正则前后中加上锚 ^ 和 $。

比如要匹配目标字符串 “abc” 或者 “bcd” 时,如果一不小心,就会写成 /^abc|bcd$/。

而位置字符和字符序列优先级要比竖杠高,

应该修改成 /^(abc|bcd)$/

5.2.2 量词连缀问题

假设,要匹配这样的字符串:

1 每个字符为 "a、“b”、“c” 任选其一,

2 字符串的长度是 3 的倍数。

此时正则不能想当然地写成 /1{3}+$/,这样会报错,说 + 前面没什么可重复的:

此时要修改成:/^([abc]{3})+$/

5.2.3 元字符转义问题

所谓元字符,就是正则中有特殊含义的字符。

所有结构里,用到的元字符总结如下:

^、$、.、*、+、?、|、\、/、(、)、[、]、{、}、=、!、:、- ,

当匹配上面的字符本身时,可以一律转义:

var string = "^$.*+?|\\/[]{}=!:-,";
var regex = /\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,/;
console.log( regex.test(string) );
// => true

其中 string 中的 \ 字符也要转义的。

5.2.3.1. 匹配 “[abc]” 和 “{3,5}”

我们知道 [abc],是个字符组。如果要匹配字符串 “[abc]” 时,该怎么办?

可以写成 /\[abc\]/,,也可以写成 /\[abc]/

括号需要前后都转义的,如 /(123)/。

5.3. 案例分析

接下来分析两个例子,一个简单的,一个复杂的。

5.3.1 身份证

正则表达式是: /^(\d{15}|\d{17}[\dxX])$/

因为竖杠 | 的优先级最低,所以正则分成了两部分 \d{15} 和 \d{17}[\dxX]。

\d{15} 表示 15 位连续数字。

\d{17}[\dxX] 表示 17 位连续数字,最后一位可以是数字,可以大小写字母 “x”。

第七章 正则表达式编程

7.1. 正则表达式的四种操作

正则表达式是匹配模式,不管如何使用正则表达式,万变不离其宗,都需要先“匹配”。

有了匹配这一基本操作后,才有其他的操作:验证、切分、提取、替换。

7.1.1. 验证

验证是正则表达式最直接的应用,比如表单验证。

在说验证之前,先要说清楚匹配是什么概念。

所谓匹配,就是看目标字符串里是否有满足匹配的子串。因此,“匹配”的本质就是“查找”。

有没有匹配,是不是匹配上,判断是否的操作,即称为“验证”。

比如,判断一个字符串中是否有数字。

//1. 使用 search:var regex = /\d/;
var string = "abc123";
console.log( !!~string.search(regex) );
// => true// 2. 使用 test:
var regex = /\d/;
var string = "abc123";
console.log( regex.test(string) );
// => true//3. 使用 match:
var regex = /\d/;
var string = "abc123";
console.log( !!string.match(regex) );
// => true// 4使用 exec:
var regex = /\d/;
var string = "abc123";
console.log( !!regex.exec(string) );
// => true其中,最常用的是 test。

7.1.2. 切分

匹配上了,我们就可以进行一些操作,比如切分。

所谓“切分”,就是把目标字符串,切成一段一段的。在 JavaScript 中使用的是 split。

比如,目标字符串是 “html,css,javascript”,按逗号来切分:

var regex = /,/;
var string = "html,css,javascript";
console.log( string.split(regex) );
// => ["html", "css", "javascript"]

又比如,如下的日期格式:

2017/06/26
2017.06.26
2017-06-26

可以使用 split “切出”年月日:

var regex = /\D/;
console.log( "2017/06/26".split(regex) );
console.log( "2017.06.26".split(regex) );
console.log( "2017-06-26".split(regex) );
// => ["2017", "06", "26"]
// => ["2017", "06", "26"]
// => ["2017", "06", "26"]

7.1.3. 提取

虽然整体匹配上了,但有时需要提取部分匹配的数据。

此时正则通常要使用分组引用(分组捕获)功能,还需要配合使用相关 API。

这里,还是以日期为例,提取出年月日。注意下面正则中的括号:

// 使用 match:var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
console.log( string.match(regex) );
// =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]// 使用 exec:
var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
console.log( regex.exec(string) );
// =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]// 使用 test
var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
regex.test(string);
console.log( RegExp.$1, RegExp.$2, RegExp.$3 );
// => "2017" "06" "26"// 使用 search:
var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
string.search(regex);
console.log( RegExp.$1, RegExp.$2, RegExp.$3 );
// => "2017" "06" "26"//使用 replace:
var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
var date = [];
string.replace(regex, function (match, year, month, day) {// 回调函数参数 可选为["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]date.push(year, month, day);
});
console.log(date);
// => ["2017", "06", "26"]其中,最常用的是 match。

#### 7.1.4. 替换

找,往往不是目的,通常下一步是为了替换。

在 JavaScript 中,使用 replace 进行替换。

比如把日期格式,从 yyyy-mm-dd 替换成 yyyy/mm/dd:

var string = "2017-06-26";
var today = new Date( string.replace(/-/g, "/") );
console.log( today );
// => Mon Jun 26 2017 00:00:00 GMT+0800 (中国标准时间)

这里只是简单地应用了一下 replace。但,replace 方法很是强大的,是需要重点掌握的。

### 7.2. 相关 API 注意要点

从上面可以看出用于正则操作的方法,共有 6 个,字符串实例 4 个,正则实例 2 个:

String#search
String#split
String#match
String#replace
RegExp#test
RegExp#exec

7.2.1. search 和 match 的参数问题

我们知道字符串实例的那 4 个方法参数都支持正则和字符串。

但 search 和 match,会把字符串转换为正则的。

var string = "2017.06.27";
console.log( string.search(".") );
// => 0
//需要修改成下列形式之一
console.log( string.search("\\.") );
console.log( string.search(/\./) );
// => 4
// => 4console.log( string.match(".") );
// => ["2", index: 0, input: "2017.06.27"]
//需要修改成下列形式之一
console.log( string.match("\\.") );
console.log( string.match(/\./) );
// => [".", index: 4, input: "2017.06.27"]
// => [".", index: 4, input: "2017.06.27"]console.log( string.split(".") );
// => ["2017", "06", "27"]console.log( string.replace(".", "/") );
// => "2017/06.27"

7.2.2. match 返回结果的格式问题

match 返回结果的格式,与正则对象是否有修饰符 g 有关。

var string = "2017.06.27";
var regex1 = /\b(\d+)\b/;
var regex2 = /\b(\d+)\b/g;
console.log( string.match(regex1) );
console.log( string.match(regex2) );
// => ["2017", "2017", index: 0, input: "2017.06.27"]
// => ["2017", "06", "27"]

没有 g,返回的是标准匹配格式,即,数组的第一个元素是整体匹配的内容,接下来是分组捕获的内容,

然 后是整体匹配的第一个下标,最后是输入的目标字符串。 有 g,返回的是所有匹配的内容。

当没有匹配时,不管有无 g,都返回 null。

7.2.3. exec 比 match 更强大

当正则没有 g 时,使用 match 返回的信息比较多。但是有 g 后,就没有关键的信息 index 了。 而 exec 方法就能解决这个问题,它能接着上一次匹配后继续匹配:

var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
console.log( regex2.exec(string) );
console.log( regex2.lastIndex);
console.log( regex2.exec(string) );
console.log( regex2.lastIndex);
console.log( regex2.exec(string) );
console.log( regex2.lastIndex);
console.log( regex2.exec(string) );
console.log( regex2.lastIndex);
// => ["2017", "2017", index: 0, input: "2017.06.27"]
// => 4
// => ["06", "06", index: 5, input: "2017.06.27"]
// => 7
// => ["27", "27", index: 8, input: "2017.06.27"]
// => 10
// => null
// => 0

其中正则实例 lastIndex 属性,表示下一次匹配开始的位置。

比如第一次匹配了 “2017”,开始下标是 0,共 4 个字符,因此这次匹配结束的位置是 3,下一次开始匹配 的位置是 4。

从上述代码看出,在使用 exec 时,经常需要配合使用 while 循环:

var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
var result;
while ( result = regex2.exec(string) ) {console.log( result, regex2.lastIndex );
}
// => ["2017", "2017", index: 0, input: "2017.06.27"] 4
// => ["06", "06", index: 5, input: "2017.06.27"] 7
// => ["27", "27", index: 8, input: "2017.06.27"] 10

7.2.4. 修饰符 g,对 exex 和 test 的影响

上面提到了正则实例的 lastIndex 属性,表示尝试匹配时,从字符串的 lastIndex 位开始去匹配。

字符串的四个方法,每次匹配时,都是从 0 开始的,即 lastIndex 属性始终不变。

而正则实例的两个方法 exec、test,当正则是全局匹配时,每一次匹配完成后,都会修改 lastIndex。下面 让我们以 test 为例,看看你是否会迷糊:

var regex = /a/g;
console.log( regex.test("a"), regex.lastIndex );
console.log( regex.test("aba"), regex.lastIndex );
console.log( regex.test("ababc"), regex.lastIndex );
// => true 1
// => true 3
// => false 0

注意上面代码中的第三次调用 test,因为这一次尝试匹配,开始从下标 lastIndex,即 3 位置处开始查 找,自然就找不到了。

如果没有 g,自然都是从字符串第 0 个字符处开始尝试匹配

var regex = /a/;
console.log( regex.test("a"), regex.lastIndex );
console.log( regex.test("aba"), regex.lastIndex );
console.log( regex.test("ababc"), regex.lastIndex );
// => true 0
// => true 0
// => true 0

7.2.5. test 整体匹配时需要使用 ^ 和 $

这个相对容易理解,因为 test 是看目标字符串中是否有子串匹配正则,即有部分匹配即可。

如果,要整体匹配,正则前后需要添加开头和结尾:

console.log( /123/.test("a123b") );
// => true
console.log( /^123$/.test("a123b") );
// => false
console.log( /^123$/.test("123") );
// => true

7.2.6. split 相关注意事项

split 方法看起来不起眼,但要注意的地方有两个的。

第一,它可以有第二个参数,表示结果数组的最大长度:

var string = "html,css,javascript";
console.log( string.split(/,/, 2) );
// =>["html", "css"]

第二,正则使用分组时,结果数组中是包含分隔符的:

var string = "html,css,javascript";
console.log( string.split(/(,)/) );
// =>["html", ",", "css", ",", "javascript"]

7.2.7. replace 是很强大的

replace 有两种使用形式,这是因为它的第二个参数,可以是字符串,也可以是函数。

当第二个参数是字符串时,如下的字符有特殊的含义:

例如,把 “2,3,5”,变成 “5=2+3”:

var result = "2,3,5".replace(/(\d+),(\d+),(\d+)/, "$3=$1+$2");
console.log(result);
// => "5=2+3"

又例如,把 “2,3,5”,变成 “222,333,555”:

var result = "2,3,5".replace(/(\d+)/g, "$&$&$&");
console.log(result);
// => "222,333,555"

再例如,把 “2+3=5”,变成 “2+3=2+3=5=5”:

var result = "2+3=5".replace(/=/, "$&$`$&$'$&");
console.log(result);
// => "2+3=2+3=5=5"

我们对最后这个进行一下说明。要把 “2+3=5”,变成 “2+3=2+3=5=5”,其实就是想办法把 = 替换成 =2+3=5=,

其中,$& 匹配的是 =, ‘匹配的是2+3,` 匹配的是 2+3,‘匹配的是2+3,’ 匹配的是 5。因此使用 “KaTeX parse error: Expected 'EOF', got '&' at position 1: &̲`KaTeX parse error: Expected 'EOF', got '&' at position 1: &̲’$&” 便达成了 目的。

当第二个参数是函数时,我们需要注意该回调函数的参数具体是什么:

"1234 2345 3456".replace(/(\d)\d{2}(\d)/g, function (match, $1, $2, index, input) {console.log([match, $1, $2, index, input]);
});
// => ["1234", "1", "4", 0, "1234 2345 3456"]
// => ["2345", "2", "5", 5, "1234 2345 3456"]
// => ["3456", "3", "6", 10, "1234 2345 3456"]

此时我们可以看到 replace 拿到的信息,并不比 exec 少。

7.2.8. 使用构造函数需要注意的问题

一般不推荐使用构造函数生成正则,而应该优先使用字面量。因为用构造函数会多写很多 \。

var string = "2017-06-27 2017.06.27 2017/06/27";
var regex = /\d{4}(-|\.|\/)\d{2}\1\d{2}/g;
console.log( string.match(regex) );
// => ["2017-06-27", "2017.06.27", "2017/06/27"]regex = new RegExp("\\d{4}(-|\\.|\\/)\\d{2}\\1\\d{2}", "g");
console.log( string.match(regex) );
// => ["2017-06-27", "2017.06.27", "2017/06/27"]

7.2.9. 修饰符

ES5 中修饰符,共 3 个:

当然正则对象也有相应的只读属性:

var regex = /\w/img;
console.log( regex.global );
console.log( regex.ignoreCase );
console.log( regex.multiline );
// => true
// => true
// => true

#### 7.2.10. source 属性

正则实例对象属性,除了 global、ingnoreCase、multiline、lastIndex 属性之外,还有一个 source 属性。

它什么时候有用呢? 比如,在构建动态的正则表达式时,可以通过查看该属性,来确认构建出的正则到底是什么:

var className = "high";
var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
console.log( regex.source )
// => (^|\s)high(\s|$) 即字符串"(^|\\s)high(\\s|$)"

#### 7.2.11. 构造函数属性

构造函数的静态属性基于所执行的最近一次正则操作而变化。

除了是 $1,…,$9 之外,还有几个不太常用的 属性(有兼容性问题):

测试代码如下:

var regex = /([abc])(\d)/g;
var string = "a1b2c3d4e5";
string.match(regex);console.log( RegExp.input );
console.log( RegExp["$_"]);
// => "a1b2c3d4e5"console.log( RegExp.lastMatch );
console.log( RegExp["$&"] );
// => "c3"console.log( RegExp.lastParen );
console.log( RegExp["$+"] );
// => "3"console.log( RegExp.leftContext );
console.log( RegExp["$`"] );
// => "a1b2"console.log( RegExp.rightContext );
console.log( RegExp["$'"] );
// => "d4e5"

7.3. 真实案例

7.3.1. 使用构造函数生成正则表达式

我们知道要优先使用字面量来创建正则,但有时正则表达式的主体是不确定的,此时可以使用构造函数来创 建

模拟 getElementsByClassName 方法,就是很能说明该问题的一个例子。

这里 getElementsByClassName 函数的实现思路是:

比如要获取 className 为 “high” 的 dom 元素;

首先生成一个正则:/(^|\s)high(\s|$)/,

然后再用其逐一验证页面上的所有dom元素的类名,拿到满足匹配的元素即可。

<p class="high">1111</p>
<p class="high">2222</p>
<p>3333</p>
<script>
function getElementsByClassName (className) {var elements = document.getElementsByTagName("*");var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");var result = [];for (var i = 0; i < elements.length; i++) {var element = elements[i];if (regex.test(element.className)) {result.push(element)}}return result;
}
var highs = getElementsByClassName('high');
highs.forEach(function (item) {item.style.color = 'red';
});
</script>

7.3.2. 使用字符串保存数据

一般情况下,我们都愿意使用数组来保存数据。但我看到有的框架中,使用的却是字符串

使用时,仍需要把字符串切分成数组。虽然不一定用到正则,但总感觉酷酷的,这里分享如下:

var utils = {};
"Boolean|Number|String|Function|Array|Date|RegExp|Object|Error".split("|").forEach(fun
ction (item) {utils["is" + item] = function (obj) {return {}.toString.call(obj) == "[object " + item + "]";};
});
console.log( utils.isArray([1, 2, 3]) );
// => true

7.3.3. if 语句中使用正则替代 &&

比如,模拟 ready 函数,即加载完毕后再执行回调(不兼容 IE 的):

var readyRE = /complete|loaded|interactive/;
function ready (callback) {if (readyRE.test(document.readyState) && document.body) {callback()}else {document.addEventListener('DOMContentLoaded',function () {callback()},false);}
};
ready(function () {alert("加载完毕!")
});

7.3.4. 使用强大的 replace

function compress (source) {var keys = {};source.replace(/([^=&]+)=([^&]*)/g, function (full, key, value) {keys[key] = (keys[key] ? keys[key] + ',' : '') + value;});var result = [];for (var key in keys) {result.push(key + '=' + keys[key]);}return result.join('&');
}
console.log( compress("a=1&b=2&a=3&b=4") );
// => "a=1,3&b=2,4"

文章参考原地址: https://zhuanlan.zhihu.com/p/29707385


  1. abc ↩︎

前端js正则表达式2相关推荐

  1. 前端开发:使用JS正则表达式校验邮箱和手机号的方法

    前言 在前端开发过程中,通过使用JS的正则表达式来校验输入的邮箱或者手机号是否正确,这也是一个非常常见的业务情景需求.尤其是在登录注册场景和有关提交邮箱和手机号相关的时候,一定需要做输入校验,这是正常 ...

  2. JS正则表达式大全(整理详细且实用)

    JS正则表达式大全(整理详细且实用) 作者: 字体:[增加 减小] 类型:转载 时间:2013-11-14 我要评论 JS正则表达式大全(整理详细且实用).需要的朋友可以过来参考下,希望对大家有所帮助 ...

  3. js 字符串替换_正则精要:玩转JS正则表达式,也许只需这一篇(建议收藏)

    0.导引 在正文开始前,先说说正则表达式是什么,为什么要用正则表达式?正则表达式在我个人看来就是一个程序可以识别的规则,有了这个规则,程序就可以帮我们判断某些字符是否符合我们的要求.但是,我们为什么要 ...

  4. 手机号判断正则php2019,2019手机号码JS正则表达式验证实例代码

    概念 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的 ...

  5. js正则表达式匹配字符串与优化过程

    前言 有时候需要实现对js源文件中的url字符串做拦截预处理,或者前端js语法高亮,或者需要对动态加载的关键源码做混淆保护,在某些步骤实现之前,有一个步骤是需要提炼出所有的合法字符串. 目标:检测源文 ...

  6. js正则表达式将中文标点转为英文标点

    js正则表达式将中文标点转为英文标点 前言 最近写前端的项目,本以为需要将中文标点转为英文标点这一功能的,所以写了这个文件,但是最后用不上了,删掉了可惜,所以发表出来,做个记录.因为没有使用上,所以不 ...

  7. JS正则表达式完整版

    目录 引言 第一章 正则表达式字符匹配攻略 1 两种模糊匹配 2. 字符组 3. 量词 4. 多选分支 5. 案例分析 第1章 小结 第二章 正则表达式位置匹配攻略 1. 什么是位置呢? 2. 如何匹 ...

  8. JS正则表达式完整教程

    引言 亲爱的读者朋友,如果你点开了这篇文章,说明你对正则很感兴趣. 想必你也了解正则的重要性,在我看来正则表达式是衡量程序员水平的一个侧面标准. 关于正则表达式的教程,网上也有很多,相信你也看了一些. ...

  9. JS正则表达式完整教程(略长)

    注:本文已经整理成电子书:<JavaScript正则迷你书> 引言 亲爱的读者朋友,如果你点开了这篇文章,说明你对正则很感兴趣. 想必你也了解正则的重要性,在我看来正则表达式是衡量程序员水 ...

  10. 前端 js实现模糊搜索

    前端 js实现模糊搜索 template <input type="text" v-model="keyWord" @input="fuzzyQ ...

最新文章

  1. -变量,进制,数据类型,标识符
  2. ScriptManager调用 无参数WebService
  3. wps不能打印_Excel表格怎么打印在一张纸上?
  4. 项目范围管理——知识要点
  5. 通过pyinotify实现文件的监控,包括监控文件是否传输完成
  6. 使用solr的完整流程
  7. 深信服安全软件产品配置概况
  8. Dreamoon Likes Coloring CodeForces - 1330C(贪心+思维)
  9. linux下lvm挂载设备找不到,linux – 如何在“找不到设备:设备未清除”错误消息后创建LVM快照...
  10. 微型计算机的使用寿命,微型计算机使用注意事项及保养的一般方法
  11. Web前端入门学习之JS基础知识梳理汇总
  12. 计算机第二道启动密码怎么设置,电脑一道密码怎么设置
  13. VMware vSphere Storage Appliance (VSA) 5.1 群集部署
  14. 64.Linux/Unix 系统编程手册(下) -- 伪终端
  15. 智慧城市智慧园区智慧路灯方案介绍
  16. (二十)美萍酒店管理系统:系统维护_系统设置_房间类型_单个添加、多个添加
  17. Dell 电脑的重装系统以及关键散热驱动建议
  18. 电脑安全模式没有修复计算机,电脑没有安全模式怎么办
  19. 刘洪波《英文字根词源精讲》完整目录
  20. 图像处理之图像去雾中文综述性论文

热门文章

  1. 中国海洋大学计算机网络考研题,2017年中国海洋大学信息科学与工程学院940计算机网络与安全考研题库...
  2. LSTM长短期记忆网络对车辆尾气排放进行预测
  3. Linux系统中CentOS光盘的挂载
  4. Simulink学习案例2
  5. 【CTS】Ubuntu下安装CTS测试环境
  6. 咨询系列主题:当我们在谈论业务架构的时候我们在谈论什么
  7. MER:高通量测序应用于病原体和害虫诊断——综述与实用性建议
  8. 基于BP神经网络的含水率预测模型matlab仿真
  9. gen-cpp/.deps/ChildService.Plo: No such file or directory
  10. 进入mariadb_MariaDB基础操作