每天一点Js(七)

来源:互联网 发布:ppt封面 知乎 编辑:程序博客网 时间:2024/05/22 08:03

JavaScript权威指南(第6版) 第一部分 javascript 语言核心 十章

        正则表达式(regular expression)是一个描述字符模式的对象。JavaScript的正则表达式语法是Perl5正则表达式语法的大型子集。

      正则表达式的定义

        JavaScript中的正则表达式用RegExp对象表示,可使用RegExp()构造函数创建RegExp对象,不过更多的是通过特殊的直接量语法创建,
        
    var pattern = /s$/;
        这段RegExp表达式匹配所有以's'结尾的字符串,用构造函数RegExp也可以定义一个与之等价的正则表达式。
    var pattern = new RegExp('s$');
        就像字符串和数字一样,程序中每个取值相同的原始类型直接量均表示同样的值。程序运行时每次遇到对象直接量(初始化表达式),注入{}和[]都会创建新对象,比如,如果在循环体中写var a = [],则每次遍历都会创建一个新的空数组。
        正则表达式直接量则与此不同,ES3规定,一个正则表达式直接量会在执行到它时转换为一个RegExp对象,同一段代码表示正则表达式直接量的每次运算都返回同一个对象。ES5做了相反的规定,同一段代码所表示的正则表达式直接量的每次运算都返回新对象
        正则表达式的模式规则由一个字符序列组成,包括所有字母和数字在内,大多数的字符按照直接量仅描述匹配的字符的。除此之外,正则表达式还有其他具有特殊语义的字符。
        以下一段代码在Firefox 3.6和 Firefox 4+中运行结果不一致,
    function getRE(){        var re = /[a-z]/;        re.foo = "bar";        return re;    }    var reg = getRE();    re2 = getRE();    console.log(reg === re2);// 在Firefox 3.6中返回true,在Firefox4中返回false    reg.foo = "baz";    console.log(re2.foo);// 在Firefox 3.6中返回"baz", 在Firefox 4+中返回"bar"

      直接量字符

        在正则表达式中,有许多标点符号具有特殊含义, ^ $ . * + ? = ! : | \ / ( ) [ ] { },如果想用这些字符直接量进行匹配,必须使用前缀\,这是一条通用规则。

      字符类

        将直接量字符单独放进方括号内就组成了字符类(character class),一个字符类可以匹配它所包含的任意字符,因此正则表达式/[a b c]/就和字符'a','b','c'中的任意一个都匹配。另外,可以通过"^"符号定义否定字符类,它匹配所有不包含在方括号内的字符。字符类可以使用连字符,表示字符范围,匹配拉丁字母表中的小写字母,可以使用/[a-z]/,要匹配拉丁字母中的任何字母和数字,要使用 /[a-zA-Z0-9]/。

      重复

        
        例如,
    /\d{2,4}/ // 匹配2~4个数字    /\w{3}\d?/ // 精确匹配三个单词和一个可选数字    /\s+java\s+/ // 匹配前后带有一个或多个空格的字符串'java'    /[^(]*/ // 匹配0个或多个非左圆括号的字符
        在使用 "*" 和 "?" 时要注意,这些字符可能匹配0个字符,因此它们允许什么都不匹配,正则表达式/a*/与字符串"bbbb"匹配,该串没有"a"。

      非贪婪的重复

        上表列出的匹配重复字符是尽可能多地匹配,而且允许后续的正则表达式继续匹配,我们称之为"贪婪的"匹配。我们同样可以使用正则表达式进行非贪婪的匹配。只须在待匹配的字符后跟随一个问号即可,"??","+?","*?","{1,5}?"。正则表达式/a+/可以匹配一个或多个连续字母a。当使用"aaa"作为匹配字符串时,正则表达式会匹配它的三个字符。但/a+?/会匹配一个字母a。
        使用非贪婪的匹配模式所得到的结果可能和期望并不一致。/a+b/,匹配一个或多个a,以及一个b。当使用"aaab"作为匹配字符串时,它可以匹配整个字符串。再看一下非贪婪匹配的版本/a+?b/,匹配"aaab"时,期望能匹配一个a和最后一个b,但实际上,这个模式匹配了整个字符串。这是因为正则表达式的模式匹配总会寻找字符串中第一个可能匹配的位置。

      选择,分组和引用

        正则表达式的语法还包括制定选择项、子表达式和引用前一子表达式的特殊字符。字符"|"用于分隔供选择的字符。例如,/ab|cd|ef/可以匹配字符串'ab'或者'cd'或者'ef'。/\d{3}|[a-z]{4}/匹配的是三位数字或者四个小写字母。选择项从左到右匹配,如果左边的选择项匹配,就忽略右边的匹配项,因此/a|ab/匹配字符串""ab"时,只能匹配第一个字符。
        正则表达式中的圆括号具有多种作用,
        将单独项组合成子表达式/java(script)/可以匹配字符串"java",其后可以有"script"也可以没有。/(ab|cd)+|ef/,可以匹配"ab"或"cd"一次或多次的重复,也可以匹配字串"ef"。
        另一个作用是在完整的模式中定义子模式,当一个正则表达式成功和目标字符串成功匹配,可以从目标串中抽出和圆括号中子模式向匹配的部分。假定我们正在检索的模式是一个或多个小写字母后面跟随了一位或多位数字,可以使用模式/[a-z]+\d+/,如果将模式的数字部分放在圆括号之内,(/[a-z]+(\d+)/)可以检索到匹配中抽取数字。
        带圆括号的表达式的另一个用途是允许在同一正则表达式的后部引用前面的子表达式,通过在字符"\"后面加一位或多位数字来实现的。这个数字指定了带圆括号的子表达式在正则表达式中的位置。例如,\1引用的是第一个带圆括号的子表达式,\3引用的是第三个带圆括号的子表达式。注意,因为子表达式可以嵌套另一个子表达式,它的位置是参与计数的左括号的位置。例如,
    /([Jj]ava([Ss]cript)?)\sis\s(fun\w*)/
        嵌套的([Ss]cript)可以用\2来指代。
        对正则表达式中前一个子表达式的引用,并不是指对子表达式模式的引用。而是指的与那个模式相匹配的文本的引用。例如,下面的正则表达式匹配的是位于单引号或双引号之内的0个或多个字符。但是,它并不要求左侧和右侧的引号匹配,
    /['"][^'"]*['"]/
        如果要匹配左侧和右侧的引号,如下,
    /(['"])[^'"]*\1/
        正则表达式不允许使用双引号括起的内容中有单引号,反之亦然。不能再字符类中使用这种引用,下面的写法是非法的???
    /(['"])[^\1]*\1/
        在正则表达式中,也可以以"(?:" 和 ")"来进行分组,考虑一下这个模式,
    /([Jj]ava(?:[Ss]script)?)\sis\s(fun\w*)/
        子表达式(?:[Ss]cript)仅仅用于分组,因此复制符号"?"可以应用到各个分组,这种改进的圆括号不生成引用,在这个正则表达式中,\2引用了与(fun\w*)匹配的文本。
        

      指定匹配位置

        正则表达式中的多个元素才能够匹配字符串的一个字符,但是想\b这样的元素不匹配某个课件的字符,它们指定匹配发生的合法我I之,我们称这样的元素为正则表达式的锚,他们将模式定位在搜搜字符串的特定位置上。最常用的锚元素是^,它用来匹配字符串的开始,锚元素$用以匹配字符串的结束。如果要匹配"JavaScript"这个单词,可以使用正则表达式/^JavaScript$/,如果想匹配"Java"这个词本身(不像在"Javascript"中作为单词的前缀),可以使用正则表达式/\s\Java\s/,匹配前后都有空格的单词"Java"。但这样做有两个问题,如果"Java"出现在字符串的开始或结尾,匹配不成功,除非开头和结尾处都有一个空格;第二个问题是,当找到了与之匹配的字符串时,它返回的匹配字符串的前端和后端都有空格,但这并不是我们想要的。因此我们使用单词的便捷\b来代替真正的空格符\s进行匹配。/\bJava\b/。元素\B将把匹配的锚点定位在不是单词的边界之处。因此,正则表达式/\B[Ss]cript/与"JavaScript"和"postscript"匹配,但不与"script"和"Scripting"匹配。
        任意正则表达式都可以作为锚点条件,如果在符号 "(?="  和")" 之间加入一个表达式,它是一个零宽断言,用以说明圆括号内的表达式必须正确匹配,但不是真正意义的匹配。比如,要匹配一个程序语言的名字,但只在其后面有冒号时才匹配,可以使用 
    /[Jj]ava([Ss]cript)?(?=\:)/
        这个正则表达式可以匹配"JavaScript: The Definitive Guide"中的"JavaScript",但是不能匹配"Java in a Nutshell"中的"Java",因为它后面没有冒号。
        带有 "(?!" 的断言是负向先行断言,用以指定接下来的字符都不必匹配,例如
    /Java(?!Script)([A-Z]\w*)/
        可以匹配"Java"后跟随一个大写字母和任意多个ASCII单词,但"Java"后面不能跟随"Script"。它可以匹配"JavaBeans",但不能匹配"Javanese","JavaScript"和"JavaScripter"。负向先行断言的意思是,在?!前面的匹配式的后面不能出现?!后面的匹配式匹配到的表达式。详情见http://http://blog.sina.com.cn/s/blog_12e4623a90101cqy0.html

       修饰符

          正则表达式的修饰符,用以说明高级匹配模式的规则,修饰符是放在"/"之外的,
        这些修饰符可以任意组合,想要不区分大小写匹配字符串中的第一个单词"java"("Java"或者"JAVA"),可以使用不区分大小写的修饰符,来定义正则表达式/\bjava\b/i。想要匹配字符串中的所有单词,需要添加修饰符g: /\bjavaa\b/gi。

      用于模式匹配的String方法

        String支持4种使用正则表达式的方法。最简单的是search(),它的参数是一个正则表达式,返回第一个与之匹配的子串的起始位置,如果找不到匹配的子串,它将返回-1,
    "JavaScript".search(/script/i);
        replace()方法用以检索与替换操作,其中第一个参数是一个正则表达式,第二个参数是要进行替换的字符串。如果设置了修饰符g,只替换所匹配的第一子串。replace()第一个参数是字符串而不是正则表达式,replace()将直接搜索这个字符串,
    text.replace(/javascript/gi, "JavaScript");
        将所有不区分大小写的javascript替换为大小写JavaScript。
        正则表达式中使用圆括号括起来的子表达式是带有从左到右的索引编号的,那么正则表达式会记忆与每个表达式匹配的文本。如果替换字符串中,出现了$加数字,那么replace()将用于指定的子表达式相匹配的文本来替换两个字符。
    var quote = /"([^"]*)"/g;    text.replace(quote, '"$1"');
        用中文半角引号替换英文引号,同时要保持引号之间的内容(存储在$1中)没有被修改。replace的第二个参数是函数,能够动态地计算替换字符串。
        match方法是最常用的String正则表达式方法,它的唯一参数就是一个通过RegExp()构造函数将其转换为正则表达式。返回结果是一个由匹配结果组成的数组。如果该正则表达式设置了修饰符g,则该方法返回的数组包含字符串中所有的匹配结果。
    "1 plus 2 equals 3".match(/\d+/g)// 返回 ["1" ,"2", "3"]
        如果这个正则表达式没有设置修饰符g,match就不会进行全局检索,它只检索第一个匹配,但即使match执行的不是全局检索,它也返回一个数组。这种情况下,数组的第一个元素就是匹配的字符串,余下的元素则是正则表达式中用圆括号括起来的子表达式。如果match()返回一个数组a,那么a[0]存放的是完整的匹配,a[1]存放的是与第一个用圆括号括起来的表达式相匹配的子串,为了与replace()方法保持一致,a[n]存放的是$n的内容。
        使用以下正则表达式解析一个URL,
    var url = /(\w+):\/\/([\w.]+)\/(\S*)/;    var text = "Visit my blog at http://www.example.com/~david";    var result = text.match(url);    if(result != null){         var fullurl = result[0];// http://www.example.com/~david         var protocol = result[1];// http         var host = result[2];// www.example.com         var paht = result[3];// ~david    }
        给字符串的match()方法传入一个非全局的正则表达式,实际上和给这个正则表达式的exec()方法传入的字符串是一模一样的,它返回的数组带有两个属性,index和input。
        String对象的最后一个和正则表达式相关的方法是split(),该方法用以将它的字符串拆分为一个子串组成的数组,使用的分隔符是split()参数,
    "123,456,789".split(","); // 返回["123", "456", "789"];
        split()方法的参数也可以是一个正则表达式,使得split()方法异常强大,例如,可以指定分隔符,允许两边可以留有任意多地空白符,
    "1, 2, 3, 4, 5".split(/\s*,\s*/);// 返回["1","2","3","4","5"]

      RegExp对象

        除了RegExp()构造函数外,RegExp对象还支持三个方法和一些属性,RegExp()构造函数带有两个字符串参数,第二个参数是可选的,第一个参数包含正则表达式的主体部分,也是正则表达式直接量中两条斜线之间的文本,如果提供第二个参数,它就是正则表达式的修饰符,只能传入g,i.m的组合。
    var zipcode = new RegExp("\\d{5}", "g");// 全局匹配字符串中的5个数字,这里使用了"\\",而不是"\"
        RegExp()构造函数非常有用,特别在需要动态创建咋和你规则表达式的时候,往往没办法通过写死在代码中的正则表达式直接量来实现。如果待检索的字符串由用户输入,就必须使用RegExp()构造函数,在程序运行时创建正则表达式。

      RegExp的属性

        每个RegExp对象都包含5个属性,属性source是一个只读的字符串,包含正则表达式的文本。属性global,是一个只读的布尔值,说明这个正则表达式是否带有修饰符g。属性ignoreCase也是一个只读的布尔值,说明正则表达式是否带有修饰符i。属性multiline是一个制度的布尔值,用以说明正则表达式是否带有修饰符m。最后一个属性,lastIndex,它是一个可读写的整数,如果匹配模式带有g修饰符,这个属性存储在整个字符串中下一次检索的开始位置,会被exec()和test()方法用到。

      RegExp方法

.       RegExp最主要的执行模式匹配的方法是exec(), RegExp方法的参数是一个字符串,而String方法的参数是一个RegExp对象。exec()方法对一个指定的字符串执行一个正则表达式,就是在一个字符串中执行匹配检索。如果没有任何匹配,返回null,如果找到匹配,返回一个数组,就像match()方法为非全局检索返回的数组一样。这个数组的第一个元素包含的是与正则表达式相匹配的字符串,余下的元素是与圆括号内子表达式相匹配的子串,属性index包含了发生匹配的字符位置,属性input引用的是正在检索的字符串。
        和match()方法不同,不管正则表达式是否具有全局修饰符g,exec()都会返回一样的数组。当调用exec()的正则表达式对象具有修饰符g时,它将把当前正则表达式对象的lastIndex属性设置为紧挨着匹配字串的字符位置。当同一个正则表达式第二次调用exec()时,它将从lastIndex属性所指示的字符处开始检索。如果exec()没有发现任何匹配结果,它会将lastIndex重置为0。这种行为可以使我们在用正则表达式匹配字符串的过程中,反复调用exec(),
    var pattern = /Java/g;    var text = "JavaSript is more fun than Java!";    var result;    while((result = pattern.exec(text)) != null){        console.log("Matched '" + result[0] + "'" + " at position " + result.index + "; next search begins at " + pattern.lastIndex);    }
        另外一个RegExp方法是test(),它比exec()更简单一些,它的参数是一个字符串,用test()对某个字符串进行检测,如果包含正则表达式的一个匹配结果,返回true:
    var pattern = /java/i;    pattern.test("JavaScript");// 返回true
        调用test()和调用exec()等价,当exec()的返回结果不是null时,test()返回true。由于这种等价性,当一个全局正则表达式调用方法test()时,它的行为和exec()相似,因为它从lastindex指定的位置处开始检索字符串,如果它找到了一个匹配结果,那么它立即设置lastIndex为当前匹配子串的结束为止。
        与exec()和test()不同的是,String()方法search(),replace()和match()并不会用到lastIndex属性。实际上,String方法只是简单地将lastIndex属性值重置为0,。如果让一个带有修饰符g的正则表达式对多个字符串执行exec()或test(),要么在每个字符串中找出所有的匹配以便将lastIndex自动重置为0,要么显式将lastIndex手动设置为0,(当最后一次检索失败时需要手动设置lastIndex)。