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

由于本课程的中心是 JavaScript,所以本文偏重讲解了「正则表达式」在 JavaScript 中的用法,并未深切「正则表达式」的详细细节。假如您尚不相识「正则表达式」,强烈引荐您先进修 正则表达式30分钟入门教程 http://deerchao.net/tutorials/regex/regex.htm 以后,再举行本课程的进修。

正则表达式(regular expression)是一个形貌字符情势的对象,运用正则表达式可以举行壮大的情势婚配和文本检索与替代功用。JavaScript 的正则表达式语法是 Perl5 的正则表达式语法的大型子集,所以关于有 Perl 编程履历的程序员来讲,进修 JavaScript 中的正则表达式是小菜一碟。

正则表达式的定义

JavaScript 中的正则表达式用 RegExp 对象示意,可以运用 RegExp() 组织函数来建立 RegExp 对象,不过 RegExp 对象更多是经由过程字面量的语法来建立,运用下面相似 Perl 的语法,就可以建立一个正则表达式。比方:

// 引荐写法
var expression = / pattern / flags ;

// 不引荐写法
var expression = new RegExp(pattern, flags);

个中的情势(pattern)部份,是包含在一对斜杠 / 之间的字符,可以是任何简朴或庞杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向援用。每一个正则表达式都可带有一或多个标志(flags),用以标明正则表达式的行动。正则表达式的婚配情势支撑以下3个标志。

  • g:示意全局(global)情势,即情势将被运用于一切字符串,而非在发明第一个婚配项时马上住手;

  • i:示意不辨别大小写(case-insensitive)情势,即在肯定婚配项时疏忽情势与字符串的大小写;

  • m:示意多行(multiline)情势,即在抵达一行文本末端时还会继续查找下一行中是不是存在与情势婚配的项。

因而,一个正则表达式就是一个情势与上述3个标志的组合体。差别组合发生差别效果,以下面的例子所示。

// 婚配字符串中一切"at"的实例
var pattern1 = /at/g;

// 婚配第一个"bat"或"cat",不辨别大小写
var pattern2 = /[bc]at/i;

// 婚配一切以"at"末端的3个字符的组合,不辨别大小写
var pattern3 = /.at/gi;

与其他言语中的正则表达式相似,情势中运用的一切元字符都必需转义。正则表达式中的元字符包含:

( [ { \ ^ $ | ) ? * + . ] }

这些元字符在正则表达式中都有一或多种特别用途,因而假如想要婚配字符串中包含的这些字符,就必需对它们举行转义。下面给出几个例子。

// 婚配第一个"bat"或"cat",不辨别大小写
var pattern1 = /[bc]at/i;

// 婚配第一个" [bc]at",不辨别大小写
var pattern2 = /\[bc\]at/i;

// 婚配一切以"at"末端的3个字符的组合,不辨别大小写
var pattern3 = /.at/gi;

// 婚配一切".at",不辨别大小写
var pattern4 = /\.at/gi;

RegExp 实例属性

RegExp 的每一个实例都具有以下属性,经由过程这些属性可以获得有关情势的种种信息。

  • global:布尔值,示意是不是设置了 g 标志。

  • ignoreCase:布尔值,示意是不是设置了 i 标志。

  • lastIndex:整数,示意最先搜刮下一个婚配项的字符位置,从0算起。

  • multiline:布尔值,示意是不是设置了 m 标志。

  • source:正则表达式的字符串示意,根据字面量情势而非传入组织函数中的字符串情势返回。

经由过程这些属性可以获知一个正则表达式的各方面信息,但却没有多大用途,由于这些信息全都包含在情势声明中。比方:

var pattern1 = /\[bc\]at/i;
console.log(pattern1.global);         // false
console.log(pattern1.ignoreCase);     // true
console.log(pattern1.multiline);      // false
console.log(pattern1.lastIndex);      // 0
console.log(pattern1.source);         // "\[bc\]at"

var pattern2 = new RegExp("\\[bc\\]at", "i");
console.log(pattern2.global);         // false
console.log(pattern2.ignoreCase);     // true
console.log(pattern2.multiline);      // false
console.log(pattern2.lastIndex);      // 0
console.log(pattern2.source);         // "\[bc\]at"

我们注重到,只管第一个情势运用的是字面量,第二个情势运用了 RegExp 组织函数,但它们的 source 属性是雷同的。可见,source 属性保留的是范例情势的字符串,即字面量情势所用的字符串。

RegExp 实例要领

RegExp 对象的重要要领是 exec(),该要领是特地为捕捉组而设想的。exec() 接收一个参数,即要运用情势的字符串,然后返回包含第一个婚配项信息的数组;或许在没有婚配项的状况下返回 null。返回的数组虽然是 Array 的实例,但包含两个分外的属性:indexinput。个中,index 示意婚配项在字符串中的位置,而 input 示意运用正则表达式的字符串。在数组中,第一项是与全部情势婚配的字符串,其他项是与情势中的捕捉组婚配的字符串(假如情势中没有捕捉组,则该数组只包含一项)。请看下面的例子。

var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;

var matches = pattern.exec(text);
console.log(matches.index);     // 0
console.log(matches.input);     // "mom and dad and baby"
console.log(matches[0]);        // "mom and dad and baby"
console.log(matches[1]);        // " and dad and baby"
console.log(matches[2]);        // " and baby"

这个例子中的情势包含两个捕捉组。最内部的捕捉组婚配 "and baby",而包含它的捕捉组婚配 "and dad" 或许 "and dad and baby"。当把字符串传入 exec() 要领中以后,发明了一个婚配项。由于全部字符串自身与情势婚配,所以返回的数组 matchsindex 属性值为 0。数组中的第一项是婚配的全部字符串,第二项包含与第一个捕捉组婚配的内容,第三项包含与第二个捕捉组婚配的内容。

关于 exec() 要领而言,纵然在情势中设置了全局标志 g,它每次也只会返回一个婚配项。在不设置全局标志的状况下,在统一个字符串上屡次挪用 exec() 将一直返回第一个婚配项的信息。而在设置全局标志的状况下,每次挪用 exec() 则都邑在字符串中继续查找新婚配项,以下面的例子所示。

var text = "cat, bat, sat, fat";

var pattern1 = /.at/;

// 非全局情势,第一次婚配
var matches = pattern1.exec(text);
console.log(matches.index);        // 0
console.log(matches[0]);           // cat
console.log(pattern1.lastIndex);   // 0

// 非全局情势,第二次婚配
matches = pattern1.exec(text);
console.log(matches.index);        // 0
console.log(matches[0]);           // cat
console.log(pattern1.lastIndex);   // 0

var pattern2 = /.at/g;

// 全局情势,第一次婚配
var matches = pattern2.exec(text);
console.log(matches.index);        // 0
console.log(matches[0]);           // cat
console.log(pattern2.lastIndex);   // 0

// 全局情势,第二次婚配
matches = pattern2.exec(text);
console.log(matches.index);        // 5
console.log(matches[0]);           // bat
console.log(pattern2.lastIndex);   // 8

这个例子中的第一个情势 pattern1 不是全局情势,因而每次挪用 exec() 返回的都是第一个婚配项 "cat"。而第二个情势 pattern2 是全局情势,因而每次挪用 exec() 都邑返回字符串中的下一个婚配项,直至搜刮到字符串末端为止。另外,还应该注重情势的 lastIndex 属性的变化状况。在全局婚配情势下,lastIndex 的值在每次挪用 exec() 后都邑增添,而在非全局情势下则一直保持稳定。

IE 的 JavaScript 实如今 lastIndex 属性上存在误差,纵然在非全局情势下,lastIndex 属性每次也会变化。

正则表达式的第二个要领是 test(),它接收一个字符串参数。在情势与该参数婚配的状况下返回 true;不然,返回 false。在只想晓得目的字符串与某个情势是不是婚配,但不须要晓得其文本内容的状况下,运用这个要领异常轻易。因而,test() 要领常常被用在 if 语句中,以下面的例子所示。

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;

if (pattern.test(text)){
    console.log("The pattern was matched.");
}

在这个例子中,我们运用正则表达式来测试了一个数字序列。假如输入的文本与情势婚配,则显现一条音讯。这类用法常常出如今考证用户输入的状况下,由于我们只想晓得输入是不是是有用,至于它为什么无效就可有可无了。

RegExp 实例继续的 toLocaleString()toString() 要领都邑返回正则表达式的字面量,与建立正则表达式的体式格局无关。比方:

var pattern = new RegExp("\\[bc\\]at", "gi");
console.log(pattern.toString());             // /\[bc\]at/gi
console.log(pattern.toLocaleString());       // /\[bc\]at/gi

纵然上例中的情势是经由过程挪用 RegExp 组织函数建立的,但 toLocaleString()toString() 要领仍然会像它是以字面量情势建立的一样显现其字符串示意。

RegExp 组织函数属性

RegExp 组织函数包含一些属性(这些属性在其他言语中被看成是静态属性)。这些属性适用于作用域中的一切正则表达式,而且基于所实行的近来一次正则表达式操纵而变化。关于这些属性的另一个奇特的地方,就是可以经由过程两种体式格局接见它们。换句话说,这些属性离别有一个长属性名和一个短属性名(Opera是破例,它不支撑短属性名)。下表列出了RegExp组织函数的属性。

长属性名短属性名申明
input$_近来一次要婚配的字符串。Opera未完成此属性。
lastMatch$&近来一次的婚配项。Opera未完成此属性。
lastParen$+近来一次婚配的捕捉组。Opera未完成此属性。
leftContext$`input字符串中lastMatch之前的文本。
multiline$*布尔值,示意是不是一切表达式都运用多行情势。IE和Opera未完成此属性。
rightContext$’Input字符串中lastMatch以后的文本。

运用这些属性可以从 exec()test() 实行的操纵中提掏出更详细的信息。请看下面的例子。

var text = "this has been a short summer";
var pattern = /(.)hort/g;

/*
 * 注重:Internet Explorer 不支撑 multiline 属性
 * Opera 不支撑 input、lastMatch、lastParen 和 multiline 属性
 */
if (pattern.test(text)){
    console.log(RegExp.input);          // this has been a short summer
    console.log(RegExp.leftContext);    // this has been a
    console.log(RegExp.rightContext);   // summer
    console.log(RegExp.lastMatch);      // short
    console.log(RegExp.lastParen);      // s
    console.log(RegExp.multiline);      // false
}

如前所述,例子运用的长属性名都可以用响应的短属性名来替代。只不过,由于这些短属性名多数不是有用的 JavaScript 标识符,因而必需经由过程方括号语法来接见它们,以下所示。

var text = "this has been a short summer";
var pattern = /(.)hort/g;

/*
 * 注重:Internet Explorer 不支撑 multiline 属性
 * Opera 不支撑 input、lastMatch、lastParen 和 multiline 属性
 */
if (pattern.test(text)){
    console.log(RegExp.$_);      // this has been a short summer
    console.log(RegExp["$`"]);   // this has been a
    console.log(RegExp["$'"]);   // summer
    console.log(RegExp["$&"]);   // short
    console.log(RegExp["$+"]);   // s
    console.log(RegExp["$*"]);   // false
}

除了上面引见的几个属性以外,另有多达9个用于存储捕捉组的组织函数属性。接见这些属性的语法是 RegExp.$1RegExp.$2RegExp.$9,离别用于存储第一、第二…第九个婚配的捕捉组。在挪用 exec()test() 要领时,这些属性会被自动添补。然后,我们就可以像下面如许来运用它们。

var text = "this has been a short summer";
var pattern = /(..)or(.)/g;

if (pattern.test(text)){
    console.log(RegExp.$1);  // sh
    console.log(RegExp.$2);  // t
}

这里建立了一个包含两个捕捉组的情势,并用该情势测试了一个字符串。纵然 test() 要领只返回一个布尔值,但 RegExp 组织函数的属性 $1$2 也会被婚配响应捕捉组的字符串自动添补。

情势的局限性

只管 JavaScript 中的正则表达式功用照样比较完整的,但仍然缺乏某些言语(特别是 Perl)所支撑的高等正则表达式特征。下面列出了 JavaScript 正则表达式所不支撑的特征。

  • 婚配字符串最先和末端的A和Z锚

  • 向后查找(lookbehind)

  • 并集和交集类

  • 原子组(atomic grouping)

  • Unicode支撑(单个字符除外,如uFFFF)

  • 定名的捕捉组

  • s(single,单行)和x(free-spacing,无距离)婚配情势

  • 前提婚配

  • 正则表达式解释

纵然存在这些限定,JavaScript 正则表达式仍然是异常壮大的,可以帮我们完成绝大多数情势婚配使命。

关卡

按请求完成以下经常使用的正则表达式。

// 应战一:数字
var pattern1 = null;  // 补全该正则表达式
console.log(pattern1.test('123')); // true
console.log(pattern1.test('abc')); // false
// 应战二:3位的数字
var pattern2 = null;  // 补全该正则表达式
console.log(pattern2.test('123'));  // true
console.log(pattern2.test('1234')); // false
// 应战三:最少3位的数字
var pattern3 = null;  // 补全该正则表达式
console.log(pattern3.test('1234')); // true
console.log(pattern3.test('12'));   // false
// 应战四:3-5位的数字
var pattern4 = null;  // 补全该正则表达式
console.log(pattern4.test('1234')); // true
console.log(pattern4.test('1'));    // false
// 应战五:由26个英文字母构成的字符串
var pattern5 = null;  // 补全该正则表达式
console.log(pattern5.test('abc'));  // true
console.log(pattern5.test('1abc')); // false
// 应战六:由数字和26个英文字母构成的字符串
var pattern6 = null;  // 补全该正则表达式
console.log(pattern6.test('1abc'));  // true
console.log(pattern6.test('_abc'));  // false
// 应战七:日期花样:年-月-日
var pattern7 = null;  // 补全该正则表达式
console.log(pattern7.test('2016-08-20'));  // true
console.log(pattern7.test('2016/08/20'));  // false
// 应战八:时候花样:小时:分钟, 24小时制
var pattern8 = null;  // 补全该正则表达式
console.log(pattern8.test('13:45'));  // true
console.log(pattern8.test('13点45')); // false
// 应战九:中国大陆身份证号,15位或18位
var pattern9 = null;  // 补全该正则表达式
console.log(pattern9.test('4223222199901090033'));  // true
console.log(pattern9.test('asdfasdfasfasdf1234'));  // false

更多

关注微信民众号「劼哥舍」复兴「答案」,猎取关卡详解。
关注 https://github.com/stone0090/javascript-lessons,猎取最新动态。

    原文作者:劼哥stone
    原文地址: https://segmentfault.com/a/1190000007196087
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞