zoukankan      html  css  js  c++  java
  • JavaScript正则表达式和模式匹配

    一、概述

    正则表达式是一个对象,用来描述字符串的模式。JavaScript用RegExp类表示正则表达式,String和RegExp两个类都定义了一些方法来使用正则表达式,完成一些基于文本的模式匹配、查找与替换等强大功能。JavaScript的正则表达式语法完全是Perl5正则表达式语法的一个子集。本文先介绍正则表达式的语法,然后介绍String和RegExp类使用正则表达式的一些方法。

    二、定义正则表达式

    在JavaScript中,正则表达式是用RegExp类表示的,所以我们当然可以使用RegExp的构造函数来定义正则表达式对象;但是就像字符串一样,我们也可以使用字面量来定义正则表达式,只不过字符串是使用一对引号包含一串字符,而正则表达式是使用一对斜杠包含("/")一串字符。

    例子:使用RegExp:var pattern=new RegExp("s$");

    上面的例子等价于:var pattern = /s$/; 他们都匹配一个s字符结尾的字符串。

    1、字符字面量

    (1)正则表达式中的字母和数字字符,都匹配他们本身。

    (2)正则表达式使用反斜杠加一个特殊字符,可以表示非字母和数字的字符。有:

    (换行),v (vertical tab), (nul字符),f(form feed), (Tab), (回车), uxxxx(4位16进制数字xxxx表示的unicode字符), xnn(2位16进制数字nn表示的拉丁字符),cX(X表示的控制字符)

    (3)标点符号:^ $ . * ! + ? = : | / ( ) [ ] { }18个字符在正则表达式中有特殊含义,如果要匹配这些字符本身,需要在前面加个反斜杠。不过其他的标点比如@符号,引号等不需要加反斜杠就表示他们本身。

    如果在匹配的时候不记得哪些需要加反斜杠,哪些不需要,一个安全的做法就是在所有需要匹配标点符号本身的情况下,都在标点符号前加上反斜杠。

    2、字符组

    多个单个字符合并一起放到一个中括号中,表示一个字符组,字符组匹配在字符组中的任意一个字符。如:/[abc]/ 匹配a,b,c中的任意一个。

    在字符组的左括号后加上一个插入符表示反字符组,反字符组匹配不在字符组中的任意一个字符,如:/[^abc]/匹配除了a,b,c的任意字符。

    字符组中可以使用连字符表示一个字符范围,如:/[a-zA-Z0-9/匹配26个大小写字母和0到9的数字中的任何一个。

    由于字符组在正则表达式中经常使用,所以JavaScript在正则表达式的语法中定义了一些特殊的转义字符表示这些字符组。对字符组归纳一下:

    [...]          --匹配括号中任意单个字符 

    [^...]        --匹配非括号中的任意单个字符,即上面的反字符组

    .               --单个句点匹配除了换行以及其他Unicode的行结束符外的任意字符

    /w             --匹配任意Ascii字符,等价于[a-zA-Z0-9_]

    /W            --匹配任意非Ascii字符,等价于[^a-zA-Z0-9_],即上面的反字符组

    /s              --匹配任意Unicode空字符

    /S              --匹配任意非Unicode空字符,即上面的反字符组

    /d              --匹配任意数字,等价于[0-9]

    /D              --匹配非数字,等价于[^0-9],即上面的反字符组

    []            --匹配单个退格字符,这个特殊情况是因为在正则表达式中有其他的意义。

    3、重复

    重复表示上一个模式可以重复的次数,重复的语法有:

    {n,m}      --上一个模式至少出现n次,但是不超过m次

    {n,}         --上一个模式至少出现n次,没有上限

    {n}          --上一个模式只能出现n次

    ?              --上一个模式出现0次或者1次,等价于{0,1}

    +             --上一个模式至少出现1次,等价于{1,}

    *             --上一个模式出现0次或者多次,等价于{0,}

    (1)非贪婪重复

    上面的重复语法表示的都是贪婪重复,贪婪重复表示在匹配过程中,在模式的其他部分仍然可以匹配的情况下,尽可能多地去匹配。

    我们可以在重复语法的后面加个问号,表示非贪婪匹配,非贪婪匹配是只要匹配到了,就不继续往下匹配了。

    比如有字符串”aaa“,用/a+/可以匹配到整个字符串”aaa“,而用/a+?/仅仅匹配了字符串的第一个字符”a“

    使用非贪婪重复,不一定能产生期望的效果,比如字符串"aaab",用/a+b/可以匹配整个字符串,如果你使用/a+?b/,可能是想匹配字符串的后两位,但是实际上该匹配返回的还是整个字符串。

    4、可选、组合、引用

    |   --用来分割可选的匹配模式,如/ab|cd|ef/  匹配ab,或者cd,或者ef,有多个可选方案时,采用从左到右的匹配顺序,找到匹配结果就停止,即使后面有更优的匹配。比如/a|ab/ 匹配字符串ab的时候,结果为a。

    ()  --圆括号表示一个组合,组合里的模式当做一个整体看待,比如/java(script)?/,匹配java或者javascript。

    正则表达式中的圆括号的另一个用途就是,用作子模式。比如/[a-z]+d+/,匹配小写字母开头,数字结尾的字符串。如果你只关心每个匹配中结尾的数字,可以使用/[a-z]+(d+)/。

    有了子模式,我们还可以通过一个反斜杠加一个数字对子模式进行引用,数字是子模式在整个模式中的索引(第几个左括号)。不过引用的不是子模式本身,而是子模式的匹配结果。

    比如:/['"][^'"]['"]/匹配的是用引号引起来的字符串,但是左右的单双引号可能不对称。如果使用子模式就可以解决这个问题:/(['"])[^'"]*1/,其中1表示对第一个子模式匹配结果的引用。

    注意,对子模式的引用,不能放到字符组里去,比如不能这样:/(['"])[^1]*1/。

    这种子模式及其引用,使用JavaScript有了强大的能力对字符串进行查找和替换。

    另外如果不想对子模式产生一个引用的索引,可以把圆括号变成(?:...),即在左括号后面加上?:,如/([jJ]ava(?:[sS]cript)?)siss(funw*)/中,2表示的是(funw*)匹配的结果

    总结一下:

    |               --可选项,表示匹配左边或者右边

    (...)           --组合,把括号中的字符组合成一个单独的单元,可以和? * + | 等一起使用。组合匹配的结果可以被记住,并在之后进行引用。

    (?:...)        --单纯的组合,不能记住匹配的结果用来对其进行引用。

                  --第n个匹配子模式的引用,n是通过从左到右数左括号的个数确定的,不过(?:除外。

    5、定义匹配的位置(锚点)

    正则表达式中的很多元素都表示匹配一个实际的字符,有一些特别的元素表示匹配字符之间的位置。比如匹配的是单词之间的边界,或者单词与字符串首尾的边界。

    像这种元素,没有确定在匹配结果中的任何字符,然而他们确定了一个匹配结果应该发生的合法位置,我们把这样的元素称为正则表达式的锚点(regular-expression anchors)。

    正则表达式锚点总结

    ^              --字符串的起始位置,在多行查找中,匹配每一行的开始

    $              --字符串的结束位置,在多行查找中,匹配每一行的结束

                 --匹配单词的边界,即:w与W之间的位置,或者w与字符串起始与结尾之间的位置。注意:要匹配一个退格符,使用[]

    B             --匹配非单词的边界,与正好相反。

    (?=p)        --匹配一个位置,该位置上接下来的字符符合模式p,匹配结果中不包含p匹配的结果。如/Java(?=:)/匹配"Java: program language"中的Java,但是不匹配"Java program language"

    (?!p)         --匹配一个位置,该位置上接下来的字符必须不符合模式p,匹配结果也不含p匹配的结果,与上面刚好相反。比如/Java(?!Script)([A-Z]w*)/匹配Java开头,后面跟一个大写字母以及任意个其他单词,但是Java后面不能跟Script。所以这个正则表达式匹配:JavaScrpt,JavaBean,但是不匹配Javabean,JavaScript,JavaScripter

    6、标识

    正则表达式最后一个语法元素是标识,标识定义个更高级别的匹配规则,标识没有定义在双斜杠之间,而是定义在第二个斜杠后面。JavaScript支持3个标识:

    i       --表示匹配是非大小写敏感的

    g      --表示匹配是全局的,在查找字符串中,所有的匹配项都必须找到

    m     --表示多行查找匹配模式,该模式下,^  $除了匹配整个查找字符串的首位外,还匹配每一行的首位位置。

    三、与模式匹配相关的String方法

    String对象支持4个方法使用正则表达式,search

    1、search:使用一个正则表达式参数,返回第一个匹配的位置,没有匹配返回-1,如:"JavaScript".search(/script/i)返回4,如果参数不是正则表达式,使用RegExp构造函数把它转成正则表达式,search不支持全局标识g,如果包含的话,会被忽略掉。

    2、replace:使用两个参数,第一个为正则表达式,第二个为替换的字符串。如果第一个参数是字符串,该函数不会像search一样把它先转成正则表达式,而是直接搜索该字符串。该函数支持全局标识g,如果使用g,则会把所有匹配的结果用第二个参数替换,否则只替换第一个匹配结果。如:text.replace(/javacript/ig,"JavaScript"),把text中的所有javascript替换成正确的大小写形式。

    如果第二个参数中包含$加一个数字的话,则替换的字符串会用匹配的结果替换掉这两个字符,然后再作为新的替换字符串去替换原来的字符串。比如包英文的引号换成中文的引号:text.replace(/"([^"]*)"/g,'”$1“');

    3、match:使用一个正则表达式参数,不是正则表达式,先转为正则表达式。返回一个字符串数组。如果正则表达式是全局模式,返回的是所以匹配结果的数组。如果正则表达式是非全局模式的,返回的也是一个数组,数组第一个元素为匹配结果,其他元素是正则表达式中子模式的匹配结果。

    4、split:使用一个正则表达式参数,匹配结果作为分隔符,把字符串分隔成字符串数组,如果参数是字符串而不是正则表达式,则直接用字符串作为分隔符。如:"123,456,234".split(",")返回["123","456","234"]

    "1,   3, 4  ,  5".split(/s*,s*/)返回["1","3","4","5"]

    四、RegExp对象

    RegExp提供了一个构造函数,3个方法,以及5个属性来定义和使用正则表达式对象。

    1、构造函数:构造函数使用1个或者2个字符串参数,第一个参数定义表达式实体,第二个参数定义表达式标识,即i,g,m及其组合。如:var zipcode=new RegExp("\d{5}","g");

    2、5个属性:

    source           --只读属性,包含正则表达式文本的字符串

    global            --只读属性,是否全局模式,true/false

    ignoreCase     --只读属性,是否忽略大小写模式,true/false

    multiline         --只读属性,是否多行模式,true/false

    lastIndex       --读写属性,整型,对于g模式下,存储下一次搜索的起始位置,方法test()和exec()会使用到。

    3、2个方法

    exec(),一个字符串参数,如果没有匹配,返回null,如果有匹配,返回匹配的字符串数组结果,类似String方法match的数组结果,数组第一个元素表示匹配结果,接下来的元素表示每一个子模式的匹配结果。在返回的结果数组对象上,新增了2个属性:index和input,index表示匹配在字符串中发生的位置,input表示查找字符串。如果正则表达式是全局模式,那么执行后会修改正则表达式对象上的lastIndex属性,表示下次搜索的起始位置。非全局模型下,lastIndex始终为0.

    如: var pattern=/Java/g;

    var text="JavaScript is more fun than Java!";

    var result;

    while((result=pattern.exec(text))!=null){

    alert(result[0]  + result.index + pattern.lastIndex);

    }

    test(),一个字符串参数,如果匹配成功,返回true,否则,返回false,该方法和exec()类似,只是返回值不同。全局模式下,执行后也会修改正则表达式的lastIndex属性,可以在一个字符串上多次调用。

  • 相关阅读:
    普通人如何做到30分钟读一本书并做完笔记?
    谈谈MySQL死锁之二 死锁检测和处理源码分析
    谈谈MySQL死锁 一
    八种架构设计模式及其优缺点概述(中)
    八种架构设计模式及其优缺点概述(上)
    轻量级开源小程序SDK发车啦
    Magicodes.IE编写多框架版本支持和执行单元测试
    Magicodes.Sms短信库的封装和集成
    Magicodes.IE之导入学生数据教程
    如何基于k8s快速搭建TeamCity(YAML分享)
  • 原文地址:https://www.cnblogs.com/winson/p/3420015.html
Copyright © 2011-2022 走看看