JavaScript正则表达式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript正则表达式相关的知识,希望对你有一定的参考价值。

之前好一段时间,自己很抵触“正则表达式”。一是,对其了解甚浅,不能很完整的掌握;再者,觉得好多需要正则的,要不网上可以找到,要不可以使用其他方式去实现。总之,对于正则毫无心得。

最近,看完了《javascript忍者秘籍》这本书,对正则有了全新的认识,自己也尝试了总结了一些,在开发中,让好多事情变得事半功倍。

正则表达式是一个拆分字符串并查询相关信息的过程。
正则表达式通常被称为一个模式(pattern),是一个用简单方式描述或者匹配一系列符合某个语法规则的字符串。

一、创建正则表达式

ECMAScript通过RegExp类型来支持正则表达式。

1. 字面量创建正则表达式

var expression = /pattern/flags;

2. 构造函数创建

var expression = new RegExp("pattern", "flags");
  • g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
  • i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
  • m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。

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

二、RegExp实例属性

RegExp每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息。

  • global:布尔值,表示是否设置了g标志.
  • ignoreCase:布尔值,表示是否设置了i标志.
  • multiline:布尔值,表示是否设置了m标志.
  • lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起.
  • source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回.

示例1:

var pattern1 = /\[bc\]at/i;
// 等价于
var pattern1 = new RegExp("\\[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

三、RegExp实例方法

1. exec()

exec()接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null。
返回的数组虽然是Array的实例,但包含两个额外的属性:index和input。
其中index表示匹配项在字符串的位置,而input表示应用正则表达式的字符串。
注意:在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。

示例1:

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

示例2:

var text = "cat, bat, sat, fat";
var pattern2 = /.at/g;

var matches = pattern2.exec(text);
console.log(matches.index);     // 0
console.log(matches[0]);        // cat
console.log(pattern2.lastIndex);    // 3

matches = pattern2.exec(text);
console.log(matches.index);     // 5
console.log(matches[0]);        // bat
console.log(pattern2.lastIndex);    // 8

2. test()

test()接受一个字符串参数。在模式与该参数匹配的情况下返回true;否则,返回false。
注意:在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便。

示例:

var text="000-00-0000";  
var pattern=/\d{3}-\d{2}-\d{4}/;
if(pattern.test(text)){
    console.log("The pattern was matched.");
}

技术分享
技术分享

四、正则表达式进阶

1. 精确匹配

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

var pattern = /test/;
pattern.test("test");   // true
pattern.test("testabc");    // true

注意:要想只允许匹配test,需/^test$/

2. 匹配一类字符

很多时候,我们不想匹配一个特定的字符,而是想匹配一个有限字符集中的某一个字符。可以通过“[]”来实现。
示例:

var pattern1 = /[abc]/; // “a”、“b“、”c“中的任何一个字符
var pattern2 = /[^abc]/;// 除了“a”、“b“、”c“以外的任何一个字符
var pattern3 = /[a-c]/; // “a”到”c“之间的的任何一个字符(等价于pattern1)

3. 转义

如果我们需要匹配[、$、^、]等特殊字符,在正则中,使用反斜杠可以对任意字符进行转义,让被转义的字符作为本身进行匹配。
示例:

var pattern1 = /\<a\/\>/;   // 匹配<a/> 
var pattern2 = /\w[email protected]\w+\.\w+/;  //简单的邮箱匹配

4. 匹配开始和匹配结束

如果正则表达式第一个字符是“^”,则表示要从字符串的开头进行匹配。如果正则表达式最后一个字符是“$”,则表示必须出现在字符串的结尾。

/^test/;    // 只能匹配以“test”开头的字符串
/test$/;   // 只能匹配以“test”结尾的字符串
/^L.*G$/;  // 只能匹配一“L”开头、“G”结尾的字符串

5. 重复出现

/a?/;           // 零次或一次
/a+/;           // 一次或多次
/a*/;           // 零次或多次
/a{4}/;         // 连续出现四次a
/a{4,7}/;       // 连续出现四次到7次
/a{4,}/;        // 连续出现四次及以上

补充:
这些重复操作符可以是贪婪的非贪婪的。默认情况下是贪婪的。
在操作符后面加一个问号(?),可以让该表达式变成非贪婪的:进行最小限度的匹配。
示例:

var pattern1 = /a+/;
var pattern2 = /a+?/;
pattern1.exec("aaa");   // ["aaa"] 匹配所有三个字符
pattern2.exec("aaa");   // ["a"] 只匹配一个字符,因为一个a字符就可以满足   

6. 预定义字符类

  • \t 水平制表符
  • \b 空格
  • \f 垂直制表符
  • \r 换页符
  • \n 回车

7. 分组

使用“()”可以进行分组,当正则表达式有一部分用括号进行分组时,它具有双重责任,同时也创建所谓的捕获。

8. 或操纵符(OR)

可以用“|”表示或的关系
示例:

/(lg)+|(ligang)+/;  // 匹配出现一次或多次的“lg”或“ligang”

9. 反向引用

在反斜杠后面加一个要引用的补货数量,该数字从1开始。
示例:

/^([dtn]a\1)/;  // dad tat nan

可以任意一个以“d”、“t”、“n”开头,且后面跟着一个“a”字符,最后跟着和第一个捕获相同的字符。
注意:

/[dtn]a[dtn]/;  // 可以是 dat dan等

上述二者不同!!!
补充:在匹配XML类型的标签元素很有用!

/<(\w+)>(.+)<\/\1>/;    // <a>click me</a>

五、捕获匹配的片段

1. 执行简单的捕获

旧版浏览器声明的透明度规则如下:filter:alpha(opacity=50);
通过正则获取其透明度的值:

var filter = "alpha(opacity=50);";
// opacity= 直到出现“)”
var result = filter.match(/opacity=([^)]+)/);
console.log(result);    // ["opacity=50", "50"]
console.log(result[1] / 100);   // 0.5

注意:match返回的数组的第一个索引值总是该匹配的完整结果,然后是每个后续捕获结果。

2. 用全局表达式进行匹配

示例1:

var html = "<div class=‘test‘><b>Hello</b></div>";
var result = html.match(/<(\w+)([^>]*?)>/); // ?为非贪婪模式(上述有提及)
console.log(result);    // ["<div class=‘test‘>", "div", " class=‘test‘"]

示例2:

var html = "<div class=‘test‘><b>Hello</b></div>";
var result = html.match(/<(\w+)([^>]*?)>/g);
console.log(result);    // ["<div class=‘test‘>", "<b>"]

说明:
示例1为非全局匹配,返回结果同上述“获取其透明度的值”,只是其包含了两个捕获。
示例2为全局匹配返回全局匹配结果,而不是每个匹配的捕获结果。

3. 捕获的引用

可以引用捕获到的匹配结果的两种方式:一是,自身匹配;二是,替换字符串。
方式一:上述“反向引用”已提及,/<(\w+)>(.+)<\/\1>/; // <a>click me</a>
方式二:将驼峰变量变为用中划线替换,这在AngularJS等框架中太常见。

如:ngClick –> ng-click

function camelCaseToDash(str){
    // $1即为正则中第一个捕获,同上述的“\1”
    return str.replace(/([A-Z])/g, "-$1").toLowerCase();
}
camelCaseToDash("ngClick"); // "ng-click"

4. 没有捕获的分组

小括号有双重责任:不仅要进行分组操作,还可以指定捕获。
如果正则表达式中存在大量的分组,就会引起很多不必要的捕获。
在开始括号后面加一个“?:”可以让其不进行捕获
示例:

var pattern1 = /((ligang-)+)good/;
var pattern2 = /((?:ligang-)+)good/;

// ["ligang-ligang-good", "ligang-ligang-", "ligang-"]
console.log("ligang-liganggood".match(pattern1)); 

// ["ligang-ligang-good", "ligang-ligang-"]
console.log("ligang-liganggood".match(pattern2));

六、利用函数进行替换

将正则表达式作为replace()方法的第一个参数时,导致在该模式的匹配元素(全局匹配的话,就是多个匹配元素)上进行替换,而不是在固定字符串上进行替换。
示例:

"ABCligangDEF".replace(/[A-Z]/g, ‘X‘);  // "XXXligangXXX"

其最大的特性是可以接受一个函数作为替换值,而不是一个固定的字符串。函数的返回值是即将替换的值。
参数列表:
(1)匹配的完整文本
(2)匹配的捕获,一个捕获对应一个参数
(3)匹配字符在源字符串中的索引
(4)源字符串
示例:将横线替换成驼峰

如:ng-click --> ngClick
function dashToCamelCase(str) {
    return str.replace(/-(\w)/g, function(all, letter){
        console.log(arguments);
        return letter.toUpperCase();
    });
}
dashToCamelCase(‘ng-click‘); // arguments --> ["-c", "c", 2, "ng-click"]

由于全局正则在每一次成功匹配的时候都会调用这样的替换函数。这种技术甚至可以超越简单的机械替换,并且可以作为字符串遍历的一种手段。
示例:压缩查询字符串
如:将foo=1&foo=2&blah=3 –> foo=1,2&blah=3

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("&");
}
compress("foo=1&foo=2&blah=3"); // "foo=1,2&blah=3"

《JavaScript高级程序设计》中提供了“提取URL的搜索字符串中的参数”的方法

如:当前浏览器地址为:http://blog.csdn.net/ligang2585116?a=1&b=2

function urlArgs(){
    var args = {};
    var query = location.search.substring(1);
    var pairs = query.split("&");
    for(var i=0; i<pairs.length; i++){
        var pos = pairs[i].indexOf(‘=‘);
        if(pos == -1) continue;
        var name = pairs[i].substring(0,pos);
        var value = pairs[i].substring(pos+1);
        value = decodeURIComponent(value);
        args[name] = value;
    }
    return args;
}
urlArgs();  // {a: "1", b: "2"}

使用上述方式实现:

function enhanceUrlArgs(query){
    var args = {};
    query.replace(/([^?&=]+)=([^&]+)/g, function(full, key, value){
        args[key] = value;
        return "";
    });
    return args;
}
enhanceUrlArgs(location.search); // {a: "1", b: "2"}

注意:上述获取搜索字符串参数的方法,都不能存在相同的key,如果有相同的key,需要求参照compress()方法进行处理!!

七、利用正则表达式解决常见问题

1. 修剪字符串

示例:修建字符串两头多余的空格

function myTrim(str) {
    // 以空格开头,后续出现零次或多次
    // 以一个或多个空格结尾
    return str.replace(/(^\s\s*)/, "").replace(/\s\s*$/, "");
    // return str.replace(/(^\s+)/, "").replace(/\s+$/, "");
}

2. 匹配换行符

示例:匹配所有字符,包括换行符

var html = "<b>Hello</b>\n<i>world</i>";
console.log(/.*/.exec(html)[0]);    // <b>Hello</b>
console.log(/[\s\S]*/.exec(html)[0]);   // <b>Hello</b>\n<i>world</i>
console.log(/(?:.|\s)/.exec(html)[0]);  // <b>Hello</b>\n<i>world</i>

最佳方案:用于匹配任何不是空白字符的字符,以及匹配任意一个空白字符,联合起来就是匹配所有字符。

3. Unicode

示例:匹配Unicode

var text = "\u674e\u521a";
console.log(text.match(/[\w\u0080-\uFFFF_-]+/)); // ["李刚"]

4. 转义字符

示例:开发人员可能将元素的id值设置为form:update,我们只能通过转义来支持

// 该正则表达式允许匹配一个单词字符,或者一个反斜杠后面跟随任意字符
var pattern = /^((\w+)|(\\.))+$/;
pattern.test("formUpdate"); // true
pattern.test("form\\:update");  // true

补充:

支持正则表达式的字符串方法:

  1. search 检索与正则表达式相匹配的值。
  2. match 找到一个或多个正则表达式的匹配。
  3. replace 替换与正则表达式匹配的子串。
  4. split 把字符串分割为字符串数组。

































以上是关于JavaScript正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript中 正则表达式的使用 及 常用正则表达式

Javascript 清理 URL 正则表达式

通过 Java 正则表达式提取 semver 版本字符串的片段

攻破难啃的骨头-正则表达式(转)

text 正则表达式片段

markdown 正则表达式模式片段