• js之学习正则表达式


    看了掘金的一个作者写的JS正则表达式完整教程 受益匪浅,感谢作者的无私奉献。在此,做下笔记。

    目录

    1. 正则表达式字符匹配

    1.1 字符组

    [ab]匹配a或者b其中一个字符

    1.1.1 常见的简写

    d: [0-9]。表示是一位数字。记忆方式:其英文是digit(数字)。
    D: [^0-9]。表示除数字外的任意字符。
    w: [0-9a-zA-Z_]。表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。
    W: [^0-9a-zA-Z_]。非单词字符。
    s: [ v f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。
    S: [^ v f]。 非空白符。
    .: [^ u2028u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式:想想省略号...中的每个点,都可以理解成占位符,表示任何类似的东西。
    [dD]、[wW]、[sS]和[^] : 任意字符

    1.1.2 排除字符组

    [^abc] 一个除"a"、"b"、"c"之外的任意一个字符,^(脱字符),表示求反

    1.2 量词

    1.2.1 简写

    {m,n} : 表示至少出现m次,至多出现n次。
    {m,} : 表示至少出现m次。
    {m} : 等价于{m,m},表示出现m次。
    ? : 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?
    +: 等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。
    *: 等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。

    1.2.2 贪婪匹配
    var regex = /d{2,5}/g;
    var string = "123 1234 12345 123456";
    console.log( string.match(regex) ); 
    // => ["123", "1234", "12345", "12345"]
    

    /d{2,5}/,表示数字连续出现2到5次。会匹配2位、3位、4位、5位连续数字,你有几位我就匹配几位,能匹配多的绝对不给你匹配少的
    有时贪婪不是一件好事,而惰性匹配,就是尽可能少的匹配:

    1.2.3 惰性匹配
    var regex = /d{2,5}?/g;
    var string = "123 1234 12345 123456";
    console.log( string.match(regex) ); 
    // => ["12", "12", "34", "12", "34", "12", "34", "56"]
    

    通过在量词后面加个问号就能实现惰性匹配,因此所有惰性匹配情形如下:

    {m,n}?
    {m,}?
    ??
    +?
    *?

    1.3 多选分支 |

    (p1|p2|p3),其中p1、p2和p3是子模式,用|(管道符)分隔,表示其中任何之一。

    var regex = /good|goodbye/g;
    var string = "goodbye";
    console.log( string.match(regex) ); 
    // => ["good"]
    
    var regex = /goodbye|good/g;
    var string = "goodbye";
    console.log( string.match(regex) ); 
    // => ["goodbye"]
    

    也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。

    1.4 案例分析

    1.4.1匹配时间 23:59 02:07

    分析:

    共4位数字,第一位数字可以为[0-2]。

    当第1位为2时,第2位可以为[0-3],其他情况时,第2位为[0-9]。

    第3位数字为[0-5],第4位为[0-9]

    var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;
    
    1.4.2匹配日期 yyyy-mm-dd

    分析:

    年,四位数字即可,可用[0-9]{4}。

    月,共12个月,分两种情况01、02、……、09和10、11、12,可用(0[1-9]|1[0-2])。

    日,最大31天,可用(0[1-9]|[12][0-9]|3[01])。

    var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
    console.log( regex.test("2017-06-10") ); 
    // => true
    
    1.4.3 window操作系统文件路径

    案例 F:studyjavascript egex egular expression.pdf

    分析:

    整体模式是: 盘符:文件夹文件夹文件夹<br/>
    盘符::[a-zA-Z]+ 盘符不能为空, 要转义

    文件夹:中间含有0或者多个文件夹的层级 文件名或者文件夹名,不能包含一些特殊字符,此时我们需要排除字符组[^:<>|"? /]来表示合法字符,另外不能为空名,至少有一个字符,也就是要使用量词+ : [^:<>|"? /+]*

    最后一部分文件夹没有 :[^:*<>|"? /+]?

    var regex = /^[a-zA-Z]:\([^\:*<>|"?
    /]+\)*([^\:*<>|"?
    /]+)?$/;
    console.log( regex.test("F:\study\javascript\regex\regular expression.pdf") ); 
    
    1.4.4 匹配id
    提取出id="container"。 ``` var regex = /id=".*"/ var string = '
    '; console.log(string.match(regex)[0]); // => id="container" class="main" ``` .是通配符,本身就匹配双引号的,而量词*又是贪婪的,当遇到container后面双引号时,不会停下来,会继续匹配,直到遇到最后一个双引号为止。 解决之道,可以使用惰性匹配: ``` var regex = /id=".*?"/ var string = '
    '; console.log(string.match(regex)[0]); // => id="container"

    //优化
    var regex = /id="[^"]*"/
    var string = '

    ';
    console.log(string.match(regex)[0]);
    // => id="container"

    
    ##### 1.4.4 匹配带86的手机号 <br/>
    13885821234 +8613885821234 08613885821234 (+86)13885821234 (086)13885821234
    

    var string="13885821234 +8613885821234 08613885821234 (+86)13885821234 (086)13885821234"
    var reg=/(+86|086|(+86)|(86))?(1[34578][0-9]{9})/g
    string.match(reg)
    //["13885821234", "+8613885821234", "08613885821234", "(+86)13885821234", "13885821234"]

    
    #### 2. <span id = "title-2">正则表达式位置匹配</span><br/>
    #### 2.1 <span id = "title-2-1">什么是位置呢?</span><br/>
    ![](http://images2017.cnblogs.com/blog/1181900/201711/1181900-20171103144747763-1652231640.png)
    
    #### 2.2 <span id = "title-2-2">如何匹配位置呢?</span><br/>
    在ES5中,共有6个锚字符:<br/>
    ^ $  B (?=p) (?!p)
    #####2.2.1  ^和$ <br/>
    ^(脱字符)匹配开头,在多行匹配中匹配行开头。<br/>
    $(美元符号)匹配结尾,在多行匹配中匹配行结尾。<br/>
    

    var result = "hello".replace(/^|$/g, '#');
    console.log(result);
    // => "#hello#"

    多行匹配
    

    var result = "I love javascript".replace(/^|$/gm, '#');
    console.log(result);
    /*

    I#

    love#

    javascript#

    */

    
    #####2.2.2  和B <br/>
    w是字母数字或者下划线的中任何一个字符<br/>
    是单词边界,具体就是w和W之间的位置,也包括w和^之间的位置,也包括w和$之间的位置。 <br/>
    

    var result = "[JS] Lesson_01.mp4".replace(//g, '#');
    console.log(result);
    // => "[#JS#] #Lesson_01#.#mp4#"

    B就是的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉,剩下的都是B的。 <br/>
    

    var result = "[JS] Lesson_01.mp4".replace(/B/g, '#');
    console.log(result);
    // => "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"

    #####2.2.3 (?=p)和(?!p) <br/>
    (?=p),其中p是一个子模式,即p前面的位置 <br/>
    (?=p),一般都理解成:要求接下来的字符与p匹配,但不能包括p的那些字符。<br/>
    而在本人看来(?=p)就与^一样好理解,就是p前面的那个位置<br/>
    (?!p),除了p前面的所有位置 <br/>
    

    var result = "hello".replace(/(?=l)/g, '#');
    console.log(result);
    // => "he#l#lo"

    var result = "hello".replace(/(?!l)/g, '#');
    console.log(result);
    // => "#h#ell#o#"

    
    #### 2.3 <span id = "title-2-3">位置的特性</span><br/>
    字符之间的位置,可以写成多个。<br/>
    把位置理解空字符,是对位置非常有效的理解方式。<br/>
    

    "hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "o" + "";
    //true
    "hello" == "" + "" + "hello"
    //true

    var result = /^^hello$$$/.test("hello");
    console.log(result);
    // => true

    var result = /(?=he)^^he(?=w)llo$$/.test("hello");
    console.log(result);
    // => true

    #### 2.4. <span id = "title-2-4">相关案例</span><br/>
    ##### 2.4.1 不匹配任何东西的正则<br/>
    /.^/   此正则要求只有一个字符,但该字符后面是开头 <br/>
    
    ##### 2.4.2 数字的千位分隔符表示法<br/>
    把"12345678",变成"12,345,678"<br/>
    弄出最后一个逗号
    

    var result = "12345678".replace(/(?=d{3}$)/g, ',')
    console.log(result);
    // => "12345,678"

    弄出所有逗号
    

    var result = "12345678".replace(/(?=(d{3})+$)/g, ',')
    console.log(result);
    // => "12345,678"

    验证发现有问题
    

    var result = "123456789".replace(/(?=(d{3})+$)/g, ',')
    console.log(result);
    // => ",123,456,789"

    怎么解决呢?我们要求匹配的到这个位置不能是开头。<br/>
    我们知道匹配开头可以使用^,但要求这个位置不是开头怎么办?<br/>
    easy,(?!^),你想到了吗?测试如下:<br/>
    

    var string1 = "12345678",
    string2 = "123456789";
    reg = /(?!^)(?=(d{3})+$)/g;

    var result = string1.replace(reg, ',')
    console.log(result);
    // => "12,345,678"

    result = string2.replace(reg, ',');
    console.log(result);
    // => "123,456,789"

    把"12345678 123456789"替换成"12,345,678 123,456,789"。 <br/>
    此时我们需要修改正则,把里面的开头^和结尾$,替换成: <br/>
    

    var string = "12345678 123456789",
    reg = /(?!)(?=(d{3})+)/g;

    var result = string.replace(reg, ',')
    console.log(result);
    // => "12,345,678 123,456,789"

    其中(?!)怎么理解呢? <br/>
    要求当前是一个位置,但不是前面的位置,其实(?!)说的就是B。 <br/>
    因此最终正则变成了:/B(?=(d{3})+)/g。 <br/>
    
    ##### 2.4.3 验证密码的问题<br/>
    密码长度6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符<br/>
    不考虑“但必须至少包括2种字符”这一条件<br/>
    

    var reg = /[1]{6,12}$/;

    必须包含数字(?=.*[0-9]) <br/>
    /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/<br/>
    对于这个正则,我们只需要弄明白(?=.*[0-9])^即可。<br/>
    分开来看就是(?=.*[0-9])和^。<br/>
    表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。<br/>
    (?=.*[0-9])表示该位置后面的字符匹配.*[0-9],即,有任何多个任意字符,后面再跟个数字。<br/>
    

    var reg = /(?=.*[0-9])[2]{6,12}$/;

    同时包含具体两种字符(?=.*[0-9])(?=.*[a-z]) <br/>
    

    var reg = /(?=.[0-9])(?=.[a-z])[3]{6,12}$/;

    我们可以把原题变成下列几种情况之一:<br/>
    
    1. 同时包含数字和小写字母
    2. 同时包含数字和大写字母
    3. 同时包含小写字母和大写字母
    4. 同时包含数字、小写字母和大写字母</br/>
    
    

    var reg = /((?=.[0-9])(?=.[a-z])|(?=.[0-9])(?=.[A-Z])|(?=.[a-z])(?=.[A-Z]))[4]{6,12}$/;
    console.log( reg.test("1234567") ); // false 全是数字
    console.log( reg.test("abcdef") ); // false 全是小写字母
    console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
    console.log( reg.test("ab23C") ); // false 不足6位
    console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
    console.log( reg.test("abcdEF234") ); // true 三者都有

    解法二
    “至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。</br/>
    那么要求“不能全部都是数字”,怎么做呢?(?!p)出马!   <br/>
    var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;
    

    var reg = /(?![0-9]{6,12}$)(?![a-z]{6,12}$)(?![A-Z]{6,12}$)[0-9A-Za-z]{6,12}$/;
    console.log( reg.test("1234567") ); // false 全是数字
    console.log( reg.test("abcdef") ); // false 全是小写字母
    console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
    console.log( reg.test("ab23C") ); // false 不足6位
    console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
    console.log( reg.test("abcdEF234") ); // true 三者都有

    #### 3.  <span id = "title-3">正则表达式括号的作用</span><br/>
    #### 3.1. <span id = "title-3-1">提取数据</span><br/>
    
    以日期为例。假设格式是yyyy-mm-dd的<br/>
    

    var regex = /(d{4})-(d{2})-(d{2})/;
    var string = "2017-06-12";
    console.log( string.match(regex) );
    // => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]

    构造函数的全局属性$1-$9,如RegExp.$1匹配结果的第一个引用()内容<br/>
    
    

    var regex = /(d{4})-(d{2})-(d{2})/;
    var string = "2017-06-12";
    console.log(RegExp.$1); // "2017"
    console.log(RegExp.$2); // "06"
    console.log(RegExp.$3); // "12"

    #### 3.2 <span id = "title-3-2">替换</span><br/>
    把yyyy-mm-dd格式,替换成mm/dd/yyyy怎么做?<br/>
    

    var regex = /(d{4})-(d{2})-(d{2})/;
    var string = "2017-06-12";
    var result = string.replace(regex, "$2/$3/$1");
    console.log(result);
    // => "06/12/2017"
    //等价于
    var regex = /(d{4})-(d{2})-(d{2})/;
    var string = "2017-06-12";
    var result = string.replace(regex, function() {
    return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1;
    });
    console.log(result);
    // => "06/12/2017"

    
    #### 3.3 <span id = "title-3-3">反向引用</span><br/>
    >
    2016-06-12
    2016/06/12
    2016.06.12
    >
    

    //缺点:但也匹配"2016-06/12"这样的数据。
    var regex = /d{4}(-|/|.)d{2}(-|/|.)d{2}/;
    //使用反向引用
    var regex = /d{4}(-|/|.)d{2}1d{2}/;

    1,表示的引用之前的那个分组(-|/|.)。不管它匹配到什么(比如-),1都匹配那个同样的具体某个字符。2和3同理<br/>
    #### 3.4 <span id = "title-3-4">非捕获分组</span><br/>
    只想要括号最原始的功能,但不会引用它,此时可以使用非捕获分组(?:p)<br/>
    

    var regex = /(?:ab)+/g;
    var string = "ababa abbb ababab";
    console.log( string.match(regex) );
    // => ["abab", "ab", "ababab"]

    
    #### 3.5 <span id = "title-3-5">相关案例</span><br/>
    ##### 3.5.1 trim()方法
    

    //法一
    function tirm(str){
    return str.replace(/^s+|s+$/g,"")
    }
    //法二 这里使用了惰性匹配?,不然也会匹配最后一个空格之前的所有空格的
    function tirm(str){
    return str.replace(/^s
    (.?)s$/g,"$1")
    }

    ##### 3.5.2  将每个单词的首字母转换为大写
    replace第一个参数是正则,第二个是字符串或者function,参数第一个是匹配正则的项,第二个是()引用返回的项,以此类推
    

    function titleize(str) {
    return str.toLowerCase().replace(/(?:^|s)w/g, function(c) {
    return c.toUpperCase();
    });
    }
    console.log( titleize('my name is epeli') );
    // => "My Name Is Epeli"

    ##### 3.5.3  驼峰化 <br/>
    -moz-transform  MozTransform <br/>
    

    function camelize(str) {
    return str.replace(/-_s?/g,function(match,c){
    return c?c.toUpperCase():''
    })
    }
    console.log( camelize('-moz-transform') );
    //中划线划
    function dasherize(str) {
    return str.replace(/([A-Z])/g, '-$1').replace(/[-_s]+/g, '-').toLowerCase();
    }
    console.log( dasherize('MozTransform') );
    // => "-moz-transform"

    单词的界定是,前面的字符可以是多个连字符、下划线以及空白符。正则后面的?的目的,是为了应对str尾部的字符可能不是单词字符,比如str是'-moz-transform    '。<br/>
    ##### 3.5.4  html转义和反转义化 <br/>
    

    // 将HTML特殊字符转换成等值的实体
    function escapeHTML(str) {
    var escapeChars = {
    '¢' : 'cent',
    '£' : 'pound',
    '¥' : 'yen',
    '€': 'euro',
    '©' :'copy',
    '®' : 'reg',
    '<' : 'lt',
    '>' : 'gt',
    '"' : 'quot',
    '&' : 'amp',
    ''' : '#39'
    };
    return str.replace(new RegExp('[' + Object.keys(escapeChars).join('') +']', 'g'), function(match) {
    return '&' + escapeChars[match] + ';';
    });
    }
    console.log( escapeHTML('

    Blah blah blah
    ') );
    // => "<div>Blah blah blah</div&gt";

    
    

    // 实体字符转换为等值的HTML。
    function unescapeHTML(str) {
    var htmlEntities = {
    nbsp: ' ',
    cent: '¢',
    pound: '£',
    yen: '¥',
    euro: '€',
    copy: '©',
    reg: '®',
    lt: '<',
    gt: '>',
    quot: '"',
    amp: '&',
    apos: '''
    };
    return str.replace(/&([^;]+);/g, function(match, key) {
    if (key in htmlEntities) {
    return htmlEntities[key];
    }
    return match;
    });
    }
    console.log( unescapeHTML('<div>Blah blah blah</div>') );
    // => "

    Blah blah blah
    "

    
    ##### 3.5.5 匹配成对html标签 <br/>
    匹配一个开标签,可以使用正则<[^>]+>,<br/>
    匹配一个闭标签,可以使用</[^>]+>,<br/>
    要求匹配成对标签,那就需要使用反向引用
    

    var regex = /<([^>]+)>[dD]*</1>/;

    
    #### 4.  <span id = "title-4">操作符优先级</span><br/>
    优先级从高到低<br/>
    
    1. 转义符 。
    2. 括号和方括号 (...)、(?:...)、(?=...)、(?!...)、[...]。
    3. 量词限定符 {m}、{m,n}、{m,}、?、*、+。
    4. 位置和序列 ^ 、$、 元字符、 一般字符。
    5. 管道符 竖杠。
    
    
    #### 5.  <span id = "title-5">元字符转义</span><br/>
    正则不会引发歧义,自然不需要转义。<br/>
    需要视情况转义的字符 ^ $ . * + ? |  / ( ) [ ] { } = ! : - ,
    
    #### 6.  <span id = "title-6">search,split,match,replace,test,exec </span><br/>
    search和match,会把接受的字符串转换为正则<br/>
    search有匹配结果返回第一个位置索引,无返回-1<br/>
    
    match:没有g,返回的是标准匹配格式,即,数组的第一个元素是整体匹配的内容,接下来是分组捕获的内容,然后是整体匹配的第一个下标,最后是输入的目标字符串。<br/>
    有g,返回的是所有匹配的内容。<br/>
    当没有匹配时,不管有无g,都返回null。<br/>
    
    exec:当正则没有g时,使用match返回的信息比较多。但是有g后,就没有关键的信息index了。
    而exec方法就能解决这个问题,它能接着上一次匹配后继续匹配:<br/>
    其中正则实例lastIndex属性,表示下一次匹配开始的位置。<br/>
    

    var string = "2017.06.27";
    var regex2 = /(d+)/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

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

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

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

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

    
    replace <br/>
    第一个参数是正则表达式<br/>
    第二个参数是字符串时,如下的字符有特殊的含义:<br/>
    >
    $1,$2,...,$99 匹配第1~99个分组里捕获的文本
    $& 匹配到的子串文本
    $` 匹配到的子串的左边文本 
    $' 匹配到的子串的右边文本
    $$ 美元符号
    >
    

    var text = "cat, bat, sat, fat";
    var result = text.replace(/(.at)/g,"word ($1)"); //word (cat), word (bat), word (sat), word (fat)

    function htmlEscape(text){
    return text.replace(/[<>"&]/g, function(match, pos, originalText){
    switch(match){
    case "<": return "<";
    case ">":return ">";
    case "&":return "&";
    case """:return """;
    }
    });
    }
    htmlEscape("<div class="gretting">

    ")
    // "<div class="gretting"></div>"


    1. 0-9A-Za-z ↩︎

    2. 0-9A-Za-z ↩︎

    3. 0-9A-Za-z ↩︎

    4. 0-9A-Za-z ↩︎

  • 相关阅读:
    Python3学习笔记(十七):requests模块
    fiddler(四)、断点(转)
    fiddler(三)、会话框添加显示请求方法栏
    PostgreSQL12同步流复制搭建-同步不生效的问题、主库恢复后,无法与新主库同步问题
    PostgreSQL的count(*) count(1) count(列名)的区别
    CentOS系统日志(转)
    常用PostgreSQL HA(高可用)工具收集
    转-性能优化中CPU、内存、磁盘IO、网络性能的依赖
    PostgreSQL查询数据库中包含某种类型的表有哪些
    PostgreSQL中with和without time zone两者有什么区别
  • 原文地址:https://www.cnblogs.com/yangwang12345/p/7777574.html
  • Copyright © 2020-2023  润新知