zoukankan      html  css  js  c++  java
  • 飞起来的正则表达式

    最近在研读一本巨著《JavaScript忍者秘籍》,里面有一篇涉及到了正则表达式。

    我在以前也曾经写过一篇正则表达式相关的文章《JavaScript与PHP中正则》,这次再做些补充。

    书中的源码可以在此下载。我将源码放到了线上,如果不想下载,可以直接访问在线网址,修改页面名就能访问到相应示例代码。

    一、术语与操作符

    1)精确匹配

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

    例如“/test/”,就表示“t”后面跟着“e”,“e”后面跟着“s”,“s”后面跟着“t”。

    2)匹配一类字符

    很多时候是想匹配一个有限字符集中的某一个字符。

    例如“[abc]”,就是要匹配“a”、“b”或“c”中的任意一个字符。

    例如“[^abc]”,就是除了“a”、“b”、“c”之外的任意字符。

    例如“[a-m]”,就是从“a”到“m”之间的任意字符。

    3)转义

    并不是所有字符和其字符字面量都是等价的。

    例如匹配“$”、“.”这种特殊字符,需要添加“”反斜杠,“$”、“.”。

    4)匹配开始与匹配结束

    经常需要确保模式匹配一个字符串的开始,或者一个字符串的结束。

    /^test/”表示以“test”开始,“/test$/”表示以“test”结尾。

    5)重复出现

    1. “”表示出现一次或不出现,例如“/t?est/”可以匹配“test”或“est”。

    2. “+”表示出现一次或多次,例如“/t+est/”可以匹配“test”、“ttest”。

    3. “*”表示出现零次或多次,例如“/t*est/”可以匹配“est”、“test”。

    4. “{number}”表示指定出现的次数,例如“/a{4}/”可以匹配“aaaa”。

    5. “{number,number}”两个数字表示重复次数区间,例如“/a{1,3}/”可以匹配“aa”、“aaa”或“aaaa”。

    6. “{number,}”次数区间的第二个数字可选,表示一个开区间。例如“/a{1,}/”匹配连续2个“a”或更多的“a”。

    7. 重复操作默认是贪婪匹配,如果要非贪婪(只做一次匹配)的可以在操作符后面加“”,例如“*?”、“+?”。

    6)预定义字符类

    有一些字符是不可能用字面量字符来表示的(例如回车)。

    还有一些经常想匹配的字符类,例如小数位数或一组空白字符。

    7)分组

    如果将操作符应用于一组术语,可以添加小括号。

    例如“/(ab)+/”匹配一个或多个“ab”字符串。

    当用括号进行分组的时候,就创建了捕获(capture)。

    8)或操作符

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

    例如“/a|b/”匹配“a”或“b”。

    9)反向引用

    在反斜杠后面加一个要引用的捕获数量,该数字从1开始,例如“1”、“2”。

    例如“/<(w+)>(.+)</1>/”匹配“<b>strong</b>”,不用反向引用是无法做到的。

    二、匹配捕获的片段

    1)执行简单的捕获

    从“filter:alpha(opacity=50);”字符串中提取透明度值,可以查看在线结果

    function getOpacity(elem) {
      var filter = elem.style.filter;
      return filter ? //#2
        filter.indexOf("opacity=") >= 0 ?
        (parseFloat(filter.match(/opacity=([^)]+)/)[1]) / 100) + "" :
        "" :
        elem.style.opacity;
    }
    
    window.onload = function() {
      assert(getOpacity(document.getElementById("opacity")) == "0.5", "The opacity of the element has been obtained.");
    };

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

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

    当应用全局表达式(g),返回值依然是一个数组,匹配所有可能的结果,而不仅仅是第一个匹配结果,在线实例查看

    var html = "<div class='test'><b>Hello</b> <i>world!</i></div>";
    
    var results = html.match(/<(/?)(w+)([^>]*?)>/); //#1
    
    assert(results[0] == "<div class='test'>", "The entire match.");
    assert(results[1] == "", "The (missing) slash.");
    assert(results[2] == "div", "The tag name.");
    assert(results[3] == " class='test'", "The attributes.");
    
    var all = html.match(/<(/?)(w+)([^>]*?)>/g); //#2
    
    assert(all[0] == "<div class='test'>", "Opening div tag.");
    assert(all[1] == "<b>", "Opening b tag.");
    assert(all[2] == "</b>", "Closing b tag.");
    assert(all[3] == "<i>", "Opening i tag.");
    assert(all[4] == "</i>", "Closing i tag.");
    assert(all[5] == "</div>", "Closing div tag.");

    在进行局部匹配时,只有一个实例被匹配了,并且该匹配的捕获结果也返回了。

    在进行全部匹配时,返回的是匹配结果的列表

    我们可以用“exec”方法,在全局正则匹配之时,恢复捕获在线实例查看

    var html = "<div class='test'><b>Hello</b> <i>world!</i></div>";
    var pattern = /<(/?)(w+)([^>]*?)>/g, match;
    var num = 0;
    
    while ((match = pattern.exec(html)) !== null) { //#1
      assert(match.length == 4, "Every match finds each tag and 3 captures.");
      num++;
    }
    
    assert(num == 6, "3 opening and 3 closing tags found.");

    exec方法保存了上次调用的状态,这样每个后续调用就可以继续下去了,直到全局匹配。每一个调用返回的都是下一个匹配及其匹配内容。

    match中的内容如下,这里就显示两个,其他类似的,下面的index就是当前字符串开始匹配的索引值。

    3)捕获的引用

    有两种方法可以引用捕获到的匹配结果:自身匹配,替换字符串。

    1. 自身匹配

    var html = "<b class='hello'>Hello</b> <i>world!</i>";
    
    var pattern = /<(w+)([^>]*)>(.*?)</1>/g; //#1
    
    var match = pattern.exec(html);
    console.log(match);
    
    assert(match[0] == "<b class='hello'>Hello</b>",
      "The entire tag, start to finish.");
    assert(match[1] == "b", "The tag name.");
    assert(match[2] == " class='hello'", "The tag attributes.");
    assert(match[3] == "Hello", "The contents of the tag.");
    
    match = pattern.exec(html);

    使用了“1”引用了表达式的第一个捕获,在本例中该捕获是标签名称,查看在线实例

    2.替换字符串

    替换字符串“replace”方法,与反向引用不一样,这里使用“$1”、“$2”语法表示每个捕获的数字。

    assert("fontFamily".replace(/([A-Z])/g, '-$1').toLowerCase() 
        == 'font-family', 'Convert the camelCase into dashed notation');

    特殊标记符“$”说明(在线调试):

    1. $i (i为数字) : 从左到右正则子表达式所匹配的文本。

    2. $&:与正则表达式匹配的全文本。

    3. $`(顿号):匹配字符串的左边文本。

    4. $’(单引号):匹配字符串的右边文本。

    5. $$:匹配$自己。

    var str = "One two three";
    str.replace(/two/, "($`)"); // “One (One ) three” 替换two之前的one
    str.replace(/w+/g, "($&)"); // “(One) (two) (three)” 替换自己
    str.replace(/two/, "($')"); // “One ( three) three” 替换two之后
    str.replace(/two/, "($$)"); // “One ($) three” 替换$自己

    4)没有捕获的分组

    小括号有双重责任:分组操作与捕获。但如果有大量的分组,就会引起不必要的捕获,查看在线实例

    var pattern = /((?:ninja-)+)sword/; //1
    
    var ninjas = "ninja-ninja-sword".match(pattern);
    
    assert(ninjas.length == 2, "Only one capture was returned.");
    assert(ninjas[1] == "ninja-ninja-",
      "Matched both words, without any extra capture.");

    该表达式只会为外层的括号创建捕获。

  • 相关阅读:
    Caliburn.Micro学习笔记(四)----IHandle<T>实现多语言功能
    linux irq 自动探测
    gpio子系统和pinctrl子系统(下)
    gpio子系统和pinctrl子系统(中)
    gpio子系统和pinctrl子系统(上)
    linux驱动基础系列--linux spi驱动框架分析
    linux驱动基础系列--linux rtc子系统
    linux驱动基础系列--Linux I2c驱动分析
    camera驱动框架分析(上)
    camera驱动框架分析(中)
  • 原文地址:https://www.cnblogs.com/strick/p/6048862.html
Copyright © 2011-2022 走看看