zoukankan      html  css  js  c++  java
  • JavaScript:正则表达式(入门篇)

    这篇文章整理自老姚的JavaScript正则迷你书第一章

    1.1. 两种模糊匹配

    模糊匹配,有两个方向上的“模糊”:横向模糊和纵向模糊。

    1.1.1. 横向模糊匹配

    横向模糊指的是,一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。

    其实现的方式是使用量词。譬如 {m,n},表示连续出现最少 m 次,最多 n 次。

    比如正则 /ab{2,5}c/ 表示匹配这样一个字符串:第一个字符是 "a",接下来是 2 到 5 个字符 "b",最后

    是字符 "c"。

    var regex = /ab{2,5}c/g;
    var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";
    console.log( string.match(regex) );
    // => ["abbc", "abbbc", "abbbbc", "abbbbbc"]
    

    案例中用的正则是 /ab{2,5}c/g,其中 g 是正则的一个修饰符。表示全局匹配,即,在目

    标字符串中按顺序找到满足匹配模式的所有子串,强调的是“所有”,而不只是“第一个”

    。g 是单词 global 的首字母。

    1.1.2. 纵向模糊匹配

    纵向模糊指的是,一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种

    可能。其实现的方式是使用字符组。譬如 [abc],表示该字符是可以字符 "a"、"b"、"c" 中的任何一个。

    比如 /a[123]b/ 可以匹配如下三种字符串: "a1b"、"a2b"、"a3b"。

    var regex = /a[123]b/g;
    var string = "a0b a1b a2b a3b a4b";
    console.log( string.match(regex) );
    // => ["a1b", "a2b", "a3b"]
    

    1.2. 字符组

    [abc],表示匹配一个字符,它可以是 "a"、"b"、"c" 之一。

    1.2.1. 范围表示法

    [123456abcdefGHIJKLM],可以写成 [1-6a-fG-M]。用连字符 - 来省略和简写。

    么要匹配 "a"、"-"、"z" 这三者中任意一个字符:可以写成如下的方式:[-az] 或 [az-] 或 [a-z]。

    即要么放在开头,要么放在结尾,要么转义。总之不会让引擎认为是范围表示法就行了

    1.2.2. 排除字符组

    纵向模糊匹配,还有一种情形就是,某位字符可以是任何东西,但就不能是 "a"、"b"、"c"。此时就是排除字符组(反义字符组)的概念。例如 [^abc],表示是一个除 "a"、"b"、"c"之外的任意一个字 符。字符组的第一位放 ^(脱字符),表示求反的概念。当然,也有相应的范围表示法。

    1.2.3. 常见的简写形式

    如果要匹配任意字符怎么办?可以使用 [dD]、[wW]、[sS] 和 [^] 中任何的一个

    1.3. 量词

    1.3.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 位连续数字。

    但是其是贪婪的,它会尽可能多的匹配。你能给我 6 个,我就要 5 个。你能给我 3 个,我就要 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"]
    

    其中 /d{2,5}?/ 表示,虽然 2 到 5 次都行,当 2 个就够的时候,就不再往下尝试了。

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

    对惰性匹配的记忆方式是:量词后面加个问号,问一问你知足了吗,你很贪婪吗?

    1.4. 多选分支

    一个模式可以实现横向和纵向模糊匹配。而多选分支可以支持多个子模式任选其一。

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

    例如要匹配字符串 "good" 和 "nice" 可以使用 /good|nice/。

    var regex = /good|nice/g;
    var string = "good idea, nice try.";
    console.log( string.match(regex) );
    // => ["good", "nice"]
    

    但有个事实我们应该注意,比如我用 /good|goodbye/,去匹配 "goodbye" 字符串时,结果是 "good"

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

    而把正则改成 /goodbye|good/,结果是:

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

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

    1.5. 案例分析

    1.5.1. 匹配 16 进制颜色值

    要求匹配:

    #ffbbad
    #Fc01DF
    #FFF
    #ffE
    

    分析:

    表示一个 16 进制字符,可以用字符组 [0-9a-fA-F]。

    其中字符可以出现 3 或 6 次,需要是用量词和分支结构。

    使用分支结构时,需要注意顺序。

    正则如下:

    var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
    var string = "#ffbbad #Fc01DF #FFF #ffE";
    console.log( string.match(regex) );
    // => ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]
    

    1.5.2. 匹配时间

    以 24 小时制为例。

    要求匹配:

    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]$/;
    console.log( regex.test("23:59") );
    console.log( regex.test("02:07") );
    // => true
    // => true
    //正则中使用了 ^ 和 $,分别表示字符串开头和结尾。
    

    如果也要求匹配 "7:9",也就是说时分前面的 "0" 可以省略。

    此时正则变成:

    var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;
    console.log( regex.test("23:59") );
    console.log( regex.test("02:07") );
    console.log( regex.test("7:9") );
    // => true
    // => true
    // => true
    

    1.5.3. 匹配日期

    比如 yyyy-mm-dd 格式为例。

    要求匹配:

    2017-06-10
    

    分析:

    年,四位数字即可,可用 [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.5.4. window 操作系统文件路径

    要求匹配:

    F:studyjavascript
    egex
    egular expression.pdf
    F:studyjavascript
    egex
    F:studyjavascript
    F:
    

    分析:

    整体模式是:

    盘符:文件夹文件夹文件夹
    
    • 其中匹配 "F:",需要使用 [a-zA-Z]:,其中盘符不区分大小写,注意 字符需要转义。

    • 文件名或者文件夹名,不能包含一些特殊字符,此时我们需要排除字符组 [^:*<>|"? /] 来表示合法

    字符。

    • 另外它们的名字不能为空名,至少有一个字符,也就是要使用量词 +。因此匹配 文件夹,可用

    [^:*<>|"? /]+。

    • 另外 文件夹,可以出现任意次。也就是 ([^:<>|"? /]+)。*

    • 路径的最后一部分可以是 文件夹,没有 ,因此需要添加 ([^:<>|"? /]+)?。

      最后拼接成了一个看起来比较复杂的正则:

      var regex = /^[a-zA-Z]:\([^\:*<>|"?
      /]+\)*([^\:*<>|"?
      /]+)?$/;
      console.log( regex.test("F:\study\javascript\regex\regular expression.pdf") );
      console.log( regex.test("F:\study\javascript\regex\") );
      console.log( regex.test("F:\study\javascript") );
      console.log( regex.test("F:\") );
      // => true
      // => true
      // => true
      // => true
      

    其中,在JavaScript 中字符串要表示字符 时,也需要转义。

    1.5.5. 匹配 id

    要求从:

    <div id="container" class="main"></div>
    

    提取出 id="container"。

    可能最开始想到的正则是:

    var regex = /id=".*"/
    var string = '<div id="container" class="main"></div>';
    console.log(string.match(regex)[0]);
    // => id="container" class="main"
    

    因为 . 是通配符,本身就匹配双引号的,而量词 * 又是贪婪的,当遇到 container 后面双引号时,是不会

    停下来,会继续匹配,直到遇到最后一个双引号为止。

    解决之道,可以使用惰性匹配:

    var regex = /id=".*?"/
    var string = '<div id="container" class="main"></div>';
    console.log(string.match(regex)[0]);
    // => id="container"
    

    当然,这样也会有个问题。效率比较低,因为其匹配原理会涉及到“回溯”这个概念(这里也只是顺便提一

    下,第四章会详细说明)。可以优化如下:

    var regex = /id="[^"]*"/
    var string = '<div id="container" class="main"></div>';
    console.log(string.match(regex)[0]);
    // => id="container"
    

    1.6. 本章小结

    掌握字符组和量词就能解决大部分常见的情形,也就是说,当你会了这二者,JavaScript 正则算是入门了。

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    (转载)C++ string中find() ,rfind() 等函数 用法总结及示例
    UVA 230 Borrowers (STL 行读入的处理 重载小于号)
    UVA 12100 打印队列(STL deque)
    uva 12096 The SetStack Computer(STL set的各种库函数 交集 并集 插入迭代器)
    uva 1592 Database (STL)
    HDU 1087 Super Jumping! Jumping! Jumping!
    hdu 1176 免费馅饼
    HDU 1003 Max Sum
    转战HDU
    hust 1227 Join Together
  • 原文地址:https://www.cnblogs.com/XF-eng/p/14395516.html
Copyright © 2011-2022 走看看