精华内容
下载资源
问答
  • php中正则表达式详解

    2017-10-09 11:31:18
    php中正则表达式详解 概述 正则表达式是一种描述字符串结果的语法规则,是一个特定的格式化模式,可以匹配、替换、截取匹配的字符串。常用的语言基本上都有正则表达式,如JavaScript、java等。其实,只有了解一...

    php中正则表达式详解

    概述

    正则表达式是一种描述字符串结果的语法规则,是一个特定的格式化模式,可以匹配、替换、截取匹配的字符串。常用的语言基本上都有正则表达式,如JavaScript、java等。其实,只有了解一种语言的正则使用,其他语言的正则使用起来,就相对简单些。文本主要围绕解决下面问题展开。

    • 有哪些常用的转义字符
    • 什么是限定符与定位符
    • 什么是单词定位符
    • 特殊字符有哪些
    • 什么是逆向引用以及怎样使用逆向引用
    • 匹配模式
    • php中怎样使用正则表达式
    • php中哪些方面需要用到正则
    • 怎样进行邮箱匹配,url匹配,手机匹配
    • 怎样使用正则替换字符串中某些字符
    • 贪婪匹配与惰性匹配区别
    • 正则表达式之回溯与固态分组
    • 正则优缺点有哪些

    正则表达式的基本知识汇总

    行定位符(^与$)

    行定位符是用来描述字符串的边界。“$”表示行结尾“^”表示行开始如"^de",表示以de开头的字符串 "de$",表示以de结尾的字符串。

    单词定界符

    我们在查找的一个单词的时候,如an是否在一个字符串”gril and body”中存在,很明显如果匹配的话,an肯定是可以匹配字符串“gril and body”匹配到,怎样才能让其匹配单词,而不是单词的一部分呢?这时候,我们可以是哟个单词定界符\b。 
    \ban\b 去匹配”gril and body”的话,就会提示匹配不到。 
    当然还有一个大写的\B,它的意思,和\b正好相反,它匹配的字符串不能使一个完整的单词,而是其他单词或字符串中的一部分。如\Ban\B。

    选择字符(|) ,表示或

    选择字符表示或的意思。如Aa|aA,表示Aa或者是aA的意思。注意使用”[]”与”|”的区别,在于”[]”只能匹配单个字符,而”|”可以匹配任意长度的字符串。在使用”[]”的时候,往往配合连接字符”-“一起使用,如[a-d],代表a或b或c或d。

    排除字符,排除操作

    正则表达式提供了”^”来表示排除不符合的字符,^一般放在[]中。如[^1-5],该字符不是1~5之间的数字。

    限定符(?*+{n,m})

    限定符主要是用来限定每个字符串出现的次数。

    限定字符 含义
    零次或一次
    * 零次或多次
    + 一次或多次
    {n} n次
    {n,} 至少n次
    {n,m} n到m次

    如(D+)表示一个或多个D

    点号操作符

    匹配任意一个字符(不包含换行符)

    表达式中的反斜杠(\)

    表达式中的反斜杠有多重意义,如转义、指定预定义的字符集、定义断言、显示不打印的字符。

    转义字符

    转义字符主要是将一些特殊字符转为普通字符。而这些常用特殊字符有”.”,”?”、”\”等。

    指定预定义的字符集

    字符 含义
    \d 任意一个十进制数字[0-9]
    \D 任意一个非十进制数字
    \s 任意一个空白字符(空格、换行符、换页符、回车符、字表符)
    \S 任意一个非空白字符
    \w 任意一个单词字符
    \W 任意个非单词字符

    ###显示不可打印的字符

    字符 含义
    \a 报警
    \b 退格
    \f 换页
    \n 换行
    \r 回车
    \t 字表符

    括号字符()

    在正则表达式中小括号的作用主要有:

    • 改变限定符如(|、* 、^)的作用范围 
      如(my|your)baby,如果没有”()”,|将匹配的是要么是my,要么是yourbaby,有了小括号,匹配的就是mybaby或yourbaby。
    • 进行分组,便于反向引用

    反向引用

    反向引用,就是依靠子表达式的”记忆”功能,匹配连续出现的字串或是字符。如(dqs)(pps)\1\2,表示匹配字符串dqsppsdqspps。在下面php应用中,我将详细展开学习反向引用。

    模式修饰符

    模式修饰符的作用是设定模式,也就是正则表达式如何解释。php中主要模式如下表:

    修饰符 说明
    i 忽略大小写
    m 多文本模式
    s 单行文本模式
    x 忽略空白字符

    正则表达式在php中应用

    php中字符串匹配

    所谓的字符串匹配,言外之意就是判断一个字符串中,是否包含或是等于另一个字符串。如果不使用正则,我们可以使用php中提供了很多方法进行这样的判断。

    不使用正则匹配

    • strstr函数 
      string strstr ( string haystack,mixedneedle [, bool $before_needle = false ]) 
      • 注1:haystackneedle是被查找的字符串。该函数区分大小写。
      • 注2:返回值是从needle开始到最后。
      • 注3:关于$needle,如果不是字符串,被当作整形来作为字符的序号来使用。
      • 注4:before_needle若为true,则返回前东西。
    • stristr函数与strstr函数相同,只是它不区分大小写
    • strpo函数 
      int strpos ( string haystack,mixedneedle [, int $offset = 0 ] ) 
      注1:可选的 offset 参数可以用来指定从 haystack 中的哪一个字符开始查找。返回的数字位置是相对于 haystack 的起始位置而言的。
    • stripos -查找字符串首次出现的位置(不区分大小定)
    • strrpos -计算指定字符串在目标字符串中最后一次出现的位置
    • strripos -计算指定字符串在目标字符串中最后一次出现的位置(不区分大小写

    使用正则进行匹配

    在php中,提供了preg_math()和preg_match_all函数进行正则匹配。关于这两个函数原型如下:


    int preg_match|preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

    搜索subject与pattern给定的正则表达式的一个匹配. 
    pattern:要搜索的模式,字符串类型。 
    subject :输入字符串。 
    matches:如果提供了参数matches,它将被填充为搜索结果。 matches[0]matches[1]将包含第一个捕获子组匹配到的文本,以此类推。 
    flags:flags可以被设置为以下标记值:PREG_OFFSET_CAPTURE 如果传递了这个标记,对于每一个出现的匹配返回时会附加字符串偏移量(相对于目标字符串的)。 注意:这会改变填充到matches参数的数组,使其每个元素成为一个由 第0个元素是匹配到的字符串,第1个元素是该匹配字符串 在目标字符串subject中的偏移量。 
    offset:通常,搜索从目标字符串的开始位置开始。可选参数 offset 用于 指定从目标字符串的某个未知开始搜索(单位是字节)。 
    返回值:preg_match()返回 pattern 的匹配次数。 它的值将是0次(不匹配)或1次,因为 preg_match()在第一次匹配后 将会停止搜索。 preg_match_all()不同于此,它会一直搜索subject直到到达结尾。 如果发生错误 preg_match()返回 FALSE。

    实例

    • 实例1 
      判断字符串”http://blog.csdn.net/hsd2012“中是否包含csdn? 
      解法一(不适用正则): 
      如果不适用正则,我们使用strstr或者strpos中任意一个都可以,在此,我将使用strstr函数,代码如下:
    $str='http://blog.csdn.net/hsd2012';
    function checkStr1($str,$str2) { return strstr1($str,$str2)?true:false; } echo checkStr($str,'csdn');

    解法二:使用正则 
    因为我们只需要判断是否存在即可,所以选择preg_match。

    $str='http://blog.csdn.net/hsd2012';
    $pattern='/csdn/';
    function checkStr2($str,$str2) { return preg_match($str2,$str)?true:false; } echo checkStr2($str,$pattern);
    • 实例2(考察单词定界符) 
      判断字符串”I am a good boy”中是否包含单词go 
      首先判断是单词,而不是字符串,因此比较的时候,需要比较是否包含’ go ‘,即在字符串go前后有一个空格。 
      解析:如果使用非正则比较,只需要调用上面的checkStr1()函数即可,注意,第二个参数前后要加一个空格,即’ go ‘。如果使用正则, 
      我们可以考虑使用单词定界符\b,那么$pattern=’/\bgo\b/’;然后调用checkStr2函数即可.
    • 例3(考察反向引用
      判断字符串”I am a good boy”中是否包含3个相同的字母 
      解析:此时,如果我们不使用正则,将会很难判断,因为字母太多了,我们不可能去将所有字母分别与该字符串比较,那样工作量也比较大。这时候涉及到了正在的反向引用。在php正则表达式中,通过\n,来表示第n次匹配到的结果。如\5代表第五次匹配到的结果。那么本题的$pattern='/(\w).*\1.*\1/'; 
      主要注意的是,在使用反向匹配的时候都需要使用(),反向匹配时,匹配()里面出现的字符或字符串。

    php中字符串替换

    不使用正则

    php中当替换字符串的时候,如果不适用正则,我们通常使用substr、mb_substr、str_replace、substr_replace关于这几个函数区别如下表。

    函数符 功能 描述
    str_replace(find,replace,string,count) 使用一个字符串替换字符串中的另一些字符。 find 必需。规定要查找的值。replace 必需。规定替换 find 中的值的值。string 必需。规定被搜索的字符串。count 可选。一个变量,对替换数进行计数。
    substr_replace(string,replacement,start,length) 把字符串的一部分替换为另一个字符串。适合用于替换自定位置的字符串。 string 必需。规定要检查的字符串。replacement 必需。规定要插入的字符串。start 必需。规定在字符串的何处开始替换。

    使用正则

    如果使用正则替换,php中提供了preg_replace _callback和preg_replace 函数,preg_replace 原型如下: 
    mixed preg_replace ( mixed pattern,mixedreplacement , mixed subject[,intlimit = -1 [, int &count]])subject中,查找pattern,使replacement 去替换,如果有limitlimitpregreplacecallbackpregreplacepregreplaceback使callbackreplacement.1hello,hello;str=’hello,中国’; 
    str=strreplace(hello,,str) 
    或是使用str=substrreplace(str,’你好’,0,5) 
    使用正则 
    pattern=/hello/;str=preg_replace (pattern,,str); 
    - 例2 
    去除字符串”gawwenngeeojjgegop”中连续相同的字母

    $str='gawwenngeeojjgegop';
    $pattern='/(.)\1/';
    $str=preg_replace($pattern,'',$str);

    解析:当然这样可能会遇到,当第一次去除了重复了字符串后,又出来重复的字符串。如字符串味’gewwenngeeojjgegop’,针对这中问题,当然,这样的话,通过判断,继续替换下去。

    • 例3 
      将字符串中”age13gegep3iorji65k65k”;中出现的连续两个数字改为第二个数字,如字符串中13被改为3
    $str='age13gegep3iorji65k65k';
    $pattern='/(\d)(\d)/';
    $str=preg_replace($pattern,'$2', $str);

    解析:$n在正则表达式外使用反向引用。n代表第几次匹配到的结果。

    php中字符串分割

    不适用正则

    php提供了explode函数去分割字符串,与其对应的是implode。关于explode原型如下: 
    array explode ( string delimiter,stringstring [, int $limit ] ) 
    delimiter:边界上的分隔字符。 
    string:输入的字符串。 
    limit:如果设置了 limit 参数并且是正数,则返回的数组包含最多 limit 个元素,而最后那个元素将包含 string 的剩余部分。如果 limit 参数是负数,则返回除了最后的 -limit 个元素外的所有元素。如果 limit 是 0,则会被当做 1。

    使用正则

    关于通过正则表达式进行字符串分割,php提供了split、preg_split 函数。preg_split() 函数,通常是比 split() 更快的替代方案。 
    array preg_split ( string pattern,stringsubject [, int limit=1[,intflags = 0 ]] )

    $str='http://blog.csdn.net/hsd2012/article/details/51152810';
    $str=explode('/', $str);

    解法二:

    $str='http://blog.csdn.net/hsd2012/article/details/51152810';
    $pattern='/\//';  /*因为/为特殊字符,需要转移*/ $str=preg_split ($pattern, $str);

    php中贪婪匹配与惰性匹配

    • 贪婪匹配:就是匹配尽可能多的字符。 
      比如,正则表达式中m.*n,它将匹配最长以m开始,n结尾的字符串。如果用它来搜索manmpndegenc的话,它将匹配到的字符串是manmpndegen而非man。可以这样想,当匹配到m的时候,它将从后面往前匹配字符n。
    • 懒惰匹配:就是匹配尽可能少的字符。 
      有的时候,我们需要并不是去贪婪匹配,而是尽可能少的去匹配。这时候,就需要将其转为惰性匹配。怎样将一个贪婪匹配转为惰性匹配呢?只需要在其后面添加一个”?”即可。如m.*?n将匹配manmpndegenc,匹配到的字符串是man。
    函数符 描述
    *? 零次或多次,但尽可能少的匹配
    +? 一次或多次,但尽可能少的匹配
    ?? 0次或1次,但尽可能少的匹配
    {n,}? 至少n次,但尽可能少的匹配
    {n,m}? n到m次 ,但尽可能少的匹配

    php正则表达式之回溯与固态分组

    回溯

    首先我们需要清楚什么是回溯,回溯就像是在走岔路口,当遇到岔路的时候就先在每个路口做一个标记。如果走了死路,就可以照原路返回,直到遇见之前所做过的标记,标记着还未尝试过的道路。如果那条路也走不能,可以继续返回,找到下一个标记,如此重复,直到找到出路,或者直到完成所有没有尝试过的路。首先我们看例题

    $str='aageacwgewcaw';
    $pattern='/a\w*c/i';
    $str=preg_match($pattern, $str);

    看到上面的程序,可能都清楚是什么意思,就是匹配$str是否包含这样一个由”a+0个或多个字母+c”不区分大小写的字符串。但是至于程序怎样去匹配的呢?匹配的过程中,回溯了多少次呢?

    匹配过程 接下来操作描述
    ‘a\w*c’中a匹配到’aageacwgewcaw’中第一个字符a \w进行下一个字符匹配
    因为\w是贪婪匹配,会一直匹配到’aageacwgewcaw’中最后一个字符w c进行下一个字符匹配时
    ‘a\w*c’中c发现没有可以匹配的 于是\w匹配进行第一次回溯,匹配到倒数第二个字符a
    ‘a\w*c’中c发现还是没有可以匹配的 于是\w匹配进行第二次回溯,匹配到倒数第三个字符c
    ‘a\w*c’中c匹配成功 匹配结束返回结果

    现在,如果我们将patternpattern=’/a\w*?c/i’;又会回溯多少次呢?正确答案是回溯四次。

    固态分组

    固态分组,目的就是减少回溯次数, 使用(?>…)括号中的匹配时如果产生了备选状态,那么一旦离开括号便会被立即 引擎抛弃掉。举个典型的例子如: ‘\w+:’这个表达式在进行匹配时的流程是这样的,会优先去匹配所有的符合\w的字符,假如字符串的末尾没有’:’,即匹配没有找到冒号,此时触发回溯机制,他会迫使前面的\w+释放字符,并且在交还的字符中重新尝试与’:’作比对。但是问题出现在这里: \w是不包含冒号的,显然无论如何都不会匹配成功,可是依照回溯机制,引擎还是得硬着头皮往前找,这就是对资源的浪费。所以我们就需要避免这种回溯,对此的方法就是将前面匹配到的内容固化,不令其存储备用状态!,那么引擎就会因为没有备用状态可用而只得结束匹配过程。大大减少回溯的次数。 
    如下代码,就不会进行回溯:

    $str='nihaoaheloo';
    $pattern='/(?>\w+):/';
    $rs=preg_match($pattern, $str);

    当然有的时候,又需慎用固态分组,如下,我要检查$str中是否包含以a结尾的字符串,很明显是包含的,但是因为使用了固态分组,反而达不到我们想要的效果

    $str='nihaoahelaa';
    $pattern1='/(?>\w+)a/';
    $pattern2='/\w+a/'; $rs=preg_match($pattern1, $str);//0 $rs=preg_match($pattern2, $str);//1

    php中其他常用字符串操作函数

    • 字符串截取截取 
      string substr ( string string,intstart [, int length])stringmbsubstr(stringstr , int start[,intlength = NULL [, string $encoding = mb_internal_encoding() ]] )
    • 字符串中大小写转换 
      strtoupper 
      strtolower 
      ucfirst 
      ucwords
    • 字符串比较 
      -strcmp、strcasecmp、strnatcmp
    • 字符串过滤
    • 字符串翻转 
      strrev($str);
    • 字符串随机排序 
      string str_shuffle ( string $str )

    补充

    怎样进行邮箱匹配,url匹配,手机匹配

    使用preg_match函数进行匹配,以下内容从TP中复制而来。 
    邮箱验证 
    pattern=/\w+([+.]\w+)@\w+([.]\w+)\.\w+([.]\w+)/’; 
    url匹配 
    pattern='/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(:\d+)?(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?/’; 
    手机验证 
    pattern=/1[3458]\d10/’;

    php中正则的优缺点

    php中正则在某些时候,能帮我们解决php函数很多困难的匹配或是替换。然后php中正则的效率,往往是我们需要考虑的,所以在某些时候,能不用正则还是尽量不去用它,除非,某些场合必须用到,或是我们能够有效减少其回溯次数。

    展开全文
  • 转自:http://apps.hi.baidu.com/share/detail/23069031 来自:http://hi.baidu.com/xghzlg/blog/item/318896358981fd2b5bb5f5b4.html public class DateCheck  ... * 正则表达式验证日期格式   

    转自:http://apps.hi.baidu.com/share/detail/23069031

    来自:http://hi.baidu.com/xghzlg/blog/item/318896358981fd2b5bb5f5b4.html

    public class DateCheck   
    {   
        /** 
         * 正则表达式验证日期格式 
         * @param args 
         */ 
    public static void main(String[] args)   
        {   
            String checkValue = "2007-02-29";   
            //String eL = "^((((1[6-9]|[2-9]\\d)\\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\\d|3[01]))|(((1[6-9]|[2-9]\\d)\\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\\d|30))|(((1[6-9]|[2-9]\\d)\\d{2})-0?2-(0?[1-9]|1\\d|2[0-8]))|(((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-)) (20|21|22|23|[0-1]?\\d):[0-5]?\\d:[0-5]?\\d$";   
               
            String eL= "^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))";   
            Pattern p = Pattern.compile(eL);    
            Matcher m = p.matcher(checkValue);    
            boolean b = m.matches();   
            if(b)   
            {   
                   
                System.out.println("格式正确");   
            }   
            else 
            {   
                System.out.println("格式错误");   
            }   

        }                                       

    }

    一、简单的日期判断(YYYY/MM/DD): 

    Java代码
    ^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$   
    ^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$ 

    二、演化的日期判断(YYYY/MM/DD| YY/MM/DD): 

    Java代码
    ^(^(\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2}$)|(^\d{4}年\d{1,2}月\d{1,2}日$)$   
    ^(^(\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2}$)|(^\d{4}年\d{1,2}月\d{1,2}日$)$ 

    三、加入闰年的判断的: 

    实例: 

               
    Java代码
    ^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$  
    ^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$

    分析: 

    1、什么是合法的日期范围?对于不同的应用场景,这个问题有不同的解释。这里采纳MSDN中的约定: 

                DateTime值类型表示值范围在公元(基督纪元)0001 年 1 月 1 日午夜 12:00:00 到公元 (C.E.) 9999 年 12 月 31 日晚上 11:59:59 之间的日期和时间。 

    2、关于闰年的阐释。 

          关于公历闰年是这样规定的:地球绕太阳公转一周叫做一回归年,一回归年长365日5时48分 46秒。因此,公历规定有平年和闰年,平年一年有365日,比回归年短0.2422日,四年共短0.9688日,故每四年增加一日,这一年有366日,就是闰年。但四年增加一日比四个回归年又多0.0312日,400年后将多3.12日,故在400年中少设3个闰年,也就是在400年中只设97个闰年,这样公历年的平均长度与回归年就相近似了。由此规定:年份是整百数的必须是400的倍数才是闰年,例如1900年、2100年就不是闰年。 

    首先需要验证年份,显然,年份范围为 0001 - 9999,匹配YYYY的正则表达式为: 

    Java代码
    [0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}  
    [0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}

    其中 [0-9] 也可以表示为 \d,但 \d 不如 [0-9] 直观,因此下面我将一直采用 [0-9] 

    用正则表达式验证日期的难点有二:一是大小月份的天数不同,二是闰年的考虑。 

    对于第一个难点,我们首先不考虑闰年,假设2月份都是28天,这样,月份和日期可以分成三种情况: 

    1、月份为 1, 3, 5, 7, 8, 10, 12,天数范围为 01 - 31,匹配MM-DD的正则表达式为: 

    Java代码
    (0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])   
    (0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]) 
    2、月份为 4, 6, 9, 11,天数范围为 01-30,匹配MM-DD的正则表达式为: 

    Java代码
    (0[469]|11)-(0[1-9]|[12][0-9]|30)   
    (0[469]|11)-(0[1-9]|[12][0-9]|30) 

    3、月份为 2,考虑平年情况,匹配MM-DD的正则表达式为: 

    Java代码
    02-(0[1-9]|[1][0-9]|2[0-8])  
    02-(0[1-9]|[1][0-9]|2[0-8])

    根据上面的成果,我们可以得到匹配平年日期格式为YYYY-MM-DD的正则表达式: 

    Java代码
    ([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))   
    ([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))) 

    接着我们来解决第二个难点:闰年的考虑。根据闰年的定义,我们可以将闰年分为两类: 

    1、能被4整除但不能被100整除的年份。寻找后两位的变化规律,可以很快得到下面的正则匹配: 

    Java代码
    ([0-9]{2})(0[48]|[2468][048]|[13579][26])   
    ([0-9]{2})(0[48]|[2468][048]|[13579][26]) 

    2、能被400整除的年份。能被400整除的数肯定能被100整除,因此后两位肯定是00,我们只要保证前两位能被4整除即可,相应的正则表达式为: 

    Java代码
    (0[48]|[2468][048]|[3579][26])00   
    (0[48]|[2468][048]|[3579][26])00 

    2.最强验证日期的正则表达式,添加了闰年的验证 

    这个日期正则表达式支持 

    YYYY-MM-DD 

    YYYY/MM/DD 

    YYYY_MM_DD 

    YYYY.MM.DD的形式 

    match : 2008-2-29 2008/02/29 

    not match : 2008-2-30   2007-2-29 

    完整的正则表达式如下: 

    Java代码
    ((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))   
    ((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)) 

    闰年的2月份有29天,因此匹配闰年日期格式为YYYY-MM-DD的正则表达式为: 

    (([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29最后,将平年和闰年的日期验证表达式合并,我们得到最终的验证日期格式为YYYY-MM-DD的正则表达式为: 

    Java代码
    (([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)   
    (([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29) 

    DD/MM/YYYY格式的正则验证表达式为: 

    Java代码
    (((0[1-9]|[12][0-9]|3[01])/((0[13578]|1[02]))|((0[1-9]|[12][0-9]|30)/(0[469]|11))|(0[1-9]|[1][0-9]|2[0-8])/(02))/([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}))|(29/02/(([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00)))   
    展开全文
  • 正则表达式

    2017-09-11 15:19:32
    没有长度限制,但是,这样的正则表达式长度往往较短。如下所示是一些正则表达式的例子: I had a \S+ day today [A-Za-z0-9\-_]{3,16} \d\d\d\d-\d\d-\d\d v(\d+)(\.\d+)* TotalMessages="(.*?)" <[^]> 这些字符串...

    1.正则表达式

    正则表达式是什么

    正则表达式只是一个字符串。没有长度限制,但是,这样的正则表达式长度往往较短。如下所示是一些正则表达式的例子:

    • I had a \S+ day today
    • [A-Za-z0-9\-_]{3,16}
    • \d\d\d\d-\d\d-\d\d
    • v(\d+)(\.\d+)*
    • TotalMessages="(.*?)"
    • <[^<>]>

    这些字符串实际上都是微型计算机程序。正则表达式的语法,实际上是一种轻量级、简洁、适用于特定领域的编程语言。记住这一点,那么你就很容易理解下面的事情:

    • 每一个正则表达式,都可以分解为一个指令序列,比如“先找到这样的字符,再找到那样的字符,再从中找到一个字符。。。”
    • 每一个正则表达式都有输入(文本)和输出(匹配规则的输出,有时是修改后的文本)
    • 正则表达式有可能出现语法错误——不是所有的字符串都是正则表达式
    • 正则表达式语法很有个性,也可以说很恐怖
    • 有时可以通过编译,使得正则表达式执行更快

    在实现中,正则表达式还有其他的特点。本文将重点讨论正则表达式的核心语法,在几乎所有的正则表达式中都可以见到这些规则。

    特别提示:正则表达式与文件通配语法无关,比如 *.xml

    2.正则表达式的基础语法

    字符

    正则表达式中包含了一系列的字符,这些字符只能匹配它们本身。有一些被称为“元字符”的特殊字符,可以匹配一些特殊规则。

    如下所示的例子中,我用红色标出了元字符。

    • I had a \S+ day today
    • [A-Za-z0-9\-_]{3,16}
    • \d\d\d\d``-``\d\d``-``\d\d
    • v(\d+)(\.\d+)*
    • TotalMessages="(.*?)"
    • <[^<>]*>

    大部分的字符,包括所有的字母和数字字符,是普通字符。也就意味着,它们只能匹配它们自己,如下所示的正则表达式:

    cat

    意味着,只能匹配一个字符串,以“c”开头,然后是字符“a”,紧跟着是字符“t”的字符串。

    到目前为止,正则表达式的功能类似于

    • 常规的Find功能
    • Java中的 String.indexOf() 函数
    • PHP中的 strpos()函数
    • 等等

    注意:不做特殊说明,正则表达式中是区分大小写的。但是,几乎所有正则表达式的实现,都会提供一个Flag用来控制是否区分大小写。

    点“.”

    我们第一个要讲解的元字符是“.”。这个符号意味着可以匹配任意一个字符。如下所示的正则表达式:

    c.t

    意味着匹配“以c开头,之后是任意一个字符,紧跟着是字母t”的字符串。

    在一段文本中,这样的正则表达式可以用来找出cat, cot, czt这样的字符串,甚至可以找出c.t这样的组合,但是不能找到ct或者是coot这样的字符串。

    使用反斜杠“\”可以忽略元字符,使得元字符的功能与普通字符一样。

    所以,正则表达式:c\.t

    表示“找到字母c,然后是一个句号(“.”),紧跟着字母t”

    反斜杠本身也是一个元字符,这意味着反斜杠本身也可以通过相似的方法变回到普通字符的用途。

    因此,正则表达式:c\\t

    表示匹配“以字符c开头,然后是一个反斜杠,紧跟着是字母t”的字符串。

    注意!在正则表达式的实现中,.是不能用于匹配换行符的。”换行符“的表示方法在不同实现中也不同。实际编程时,请参考相关文档。在本文中,我认为.是可以匹配任意字符的。实现环境通常会提供一个Flag标志位,来控制这一点。

    字符类

    字符类是一组在方括号内的字符,表示可以匹配其中的任何一个字符。

    • 正则表达式c[aeiou]t,表示可以匹配的字符串是”以c开头,接着是aeiou中的任何一个字符,最后以t结尾”。在文本的实际应用中,这样的正则表达式可以匹配:cat,cet,cit,cot,cut五种字符串。
    • 正则表达式[0123456789]表示匹配任意一个整数。
    • 正则表达式[a]表示匹配单字符a。

    包含忽略字符的例子

    • a表示匹配字符串[a]
    • [\[\]\ab]表示匹配的字符为”[“或者”]”或者”a”,或者”b”
    • [\`[]`]表示匹配的字符为”\”或者 “[”或者”]”

    在字符类中,字符的重复和出现顺序并不重要。[dabaaabcc]与[abc]是相同的

    重要提示:字符类中和字符类外的规则有时不同,一些字符在字符类中是元字符,在字符类外是普通字符。一些字符正好相反。还有一些字符在字符类中和字符类外都是元字符,这要视情况而定!

    比如,.表示匹配任意一个字符,而[.]表示匹配一个全角句号。这不是一回事!

    字符类的范围

    在字符集中,你可以通过使用短横线来表示匹配字母或数字的范围。

    • [b-f]与[b,c,d,e,f]相同,都是匹配一个字符”b”或”c”或”d”或”e”或”f”
    • [A-Z]与[ABCDEFGHIJKLMNOPQRSTUVWXYZ]相同,都是匹配任意一个大写字母。
    • [1-9]与[123456789]相同,都是匹配任意一个非零数字。

    练习

    使用目前我们已经讲解的正则表达式相关知识,在字典中匹配找到含有最多连续元音的单词,同时找到含有最多连续辅音的单词。

    答案

    [aeiou][aeiou][aeiou][aeiou][aeiou][aeiou] 这样的正则表达式,可以匹配连续含有六个元音的单词,比如 euouaeeuouaes

    同样的,恐怖的正则表达式``[bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz] 可以找到连续含有十个辅音的单词sulphhydryls.

    下文中,我们会讲解,怎样有效缩短这样的正则表达式长度。

    在字符类之外,短横线没有特殊含义。正则表达式a-z,表示匹配字符串“以a开头,然后是一个短横线,以z结尾”。

    范围和单独的字符可能在一个字符类中同时出现:

    • [0-9.,]表明匹配一个数字,或者一个全角句号,或者一个逗号
    • [0-9a-fA-F]意味着匹配一个十六进制数
    • [a-zA-Z0-9-]意味着匹配一个字母、数字或者一个短横线

    练习

    使用已经介绍过的正则表达式知识,匹配YYYY-MM-DD格式的日期。

    答案

    [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].

    同样的,下文中,我们会介绍怎样有效减少这样的正则表达式长度。

    虽然你可以尝试在正则表达式中使用一些非字母或数字作为范围的最后一个符号,比如abc[!-/]def,但是这并不是在每种实现中都合法。即使这样的语法是合法的,这样的语义也是模糊的。最好不要这样使用。

    同时,你必须谨慎选择范围的边界值。即使[A-z]在你使用的实现中,是合法的,也可能会产生无法预料的运行结果。(注意,在z到a之间,是有字符存在的)

    注意:范围的字符值代表的是字符而已,并不能代表数值范围,比如[1-31]表示匹配一个数字,是1或者2或者3,而不是匹配一个数值在1到31之间的数。

    字符类的反义

    你可以在字符类的起始位放一个反义符。

    • [^a]表示匹配任何不是“a”的字符
    • [^a-zA-Z0-9]表示匹配任何不是字母也不是数字的字符
    • [\^abc]匹配一个为“^”或者a或者b或者c的字符
    • [^\^]表示匹配任何不为“^”的字符

    练习

    在字典中,找到一个不满足“在e之前有i,但是没有c”的例子。

    答案

    cie和[^c]ei都要可以找到很多这样的例子,比如ancient,science,viel,weigh

    转义字符类

    \d这个正则表达式与[0-9]作用相同,都是匹配任何一个数字。(要匹配\d,应该使用正则表达式\\d)

    \w与[0-9A-Za-z]相同,都表示匹配一个数字或字母字符

    \s意味着匹配一个空字符(空格,制表符,回车或者换行)

    另外

    • \D与[0-9]相同,表示匹配一个非数字字符。
    • \W与[0-9A-Za-z]相同,表示匹配一个非数字同时不是字母的字符。
    • \S表示匹配一个非空字符。

    这些是你必须掌握的字符。你可能已经注意到了,一个全角句号“.”也是一个字符类,可以匹配任意一个字符。

    很多正则表达式的实现中,提供了更多的字符类,或者是标志位在ASCII码的基础上,扩展现有的字符类。

    特别提示:统一字符集中包含除了0至9之外的更多数字字符,同样的,也包含更多的空字符和字母字符。实际使用正则表达式时,请仔细查看相关文档。

    练习

    简化正则表达式 [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].

    答案

    \d\d\d\d-\d\d-\d\d.

    重复

    在字符或字符集之后,你可以使用{ }大括号来表示重复

    • 正则表达式a{1}与a意思相同,都表示匹配字母a
    • a{3}表示匹配字符串“aaa”
    • a{0}表示匹配空字符串。从这个正则表达式本身来看,它毫无意义。如果你对任何文本执行这样的正则表达式,你可以定位到搜索的起始位置,即使文本为空。
    • a\{2\}表示匹配字符串“a{2}”
    • 在字符类中,大括号没有特殊含义。[{}]表示匹配一个左边的大括号,或者一个右边的大括号

    练习

    简化下面的正则表达式

    • z.......z
    • \d\d\d\d-\d\d-\d\d
    • [aeiou][aeiou][aeiou][aeiou][aeiou][aeiou]
    • [bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz]

    答案

    • z.{7}z
    • \d{4}-\d{2}-\d{2}
    • [aeiou]{6}
    • [bcdfghjklmnpqrstvwxyz]{10}

    注意:重复字符是没有记忆性的,比如[abc]{2}表示先匹配”a或者b或者c”,再匹配”a或者b或者c”,与匹配”aa或者ab或者ac或者ba或者bb或者bc或者ca或者cb或者cc“一样。[abc]{2}并不能表示匹配”aa或者bb或者cc“

    指定重复次数范围

    重复次数是可以指定范围的

    • x{4,4}与x{4}相同
    • colou{0,1}r表示匹配colour或者color
    • a{3,5}表示匹配aaaaa或者aaaa或者aaa

    注意这样的正则表达式会优先匹配最长字符串,比如输入 I had an aaaaawful day``会匹配单词aaaaawful中的aaaaa,而不会匹配其中的aaa。

    重复次数是可以有范围的,但是有时候这样的方法也不能找到最佳答案。如果你的输入文本是I had an aaawful daaaaay那么在第一次匹配时,只能找到aaawful,只有再次执行匹配时才能找到daaaaay中的aaaaa.

    重复次数的范围可以是开区间

    • a{1,}表示匹配一个或一个以上的连续字符a。依然是匹配最长字符串。当找到第一个a之后,正则表达式会尝试匹配尽量多个的连续字母a。
    • .{0,}表示匹配任意内容。无论你输入的文本是什么,即使是一个空字符串,这个正则表达式都会成功匹配全文并返回结果。

    练习

    使用正则表达式找到双引号。要求输入字符串可能包含任意个字符。

    调整你的正则表达式使得在一对双引号中间不再包含其他的双引号。

    答案

    ".{0,}", 然后 "[^"]{0,}".

    关于重复的转义字符

    ?与{0,1}相同,比如,colou?r表示匹配colour或者color

    与{0,}相同。比如,.表示匹配任意内容

    +与{1,}相同。比如,\w+表示匹配一个词。其中”一个词”表示由一个或一个以上的字符组成的字符串,比如_var或者AccountName1.

    这些是你必须知道的常用转义字符,除此之外还有:

    • \?*+ 表示匹配字符串”?*+”
    • [?+]表示匹配一个问号,或者一个号,或者一个加号

    练习

    简化下列的正则表达式:

    • ".{0,}" and "[^"]{0,}"
    • x?x?x?
    • y*y*
    • z+z+z+z+

    答案

    • ".*" and "[^"]*"
    • x{0,3}
    • y*
    • z{4,}

    练习

    写出正则表达式,寻找由非字母字符分隔的两个单词。如果是三个呢?六个呢?

    \w+\W+\w+, \w+\W+\w+\W+\w+, \w+\W+\w+\W+\w+\W+\w+\W+\w+\W+\w+.

    下文中,我们将简化这个正则表达式。

    非贪婪匹配

    正则表达式 “.*” 表示匹配双引号,之后是任意内容,之后再匹配一个双引号。注意,其中匹配任意内容也可以是双引号。通常情况下,这并不是很有用。通过在句尾加上一个问号,可以使得字符串重复不再匹配最长字符。

    • \d{4,5}?表示匹配\d\d\d\d或者\d\d\d\d\d。也就是和\d{4}一样
    • colou??r与colou{0,1}r相同,表示找到color或者colour。这与colou?r一样。
    • “.*?”表示先匹配一个双引号,然后匹配最少的字符,然后是一个双引号,与上面两个例子不同,这很有用。

    选择匹配

    你可以使用|来分隔可以匹配的不同选择:

    • cat|dog表示匹配”cat”或者”dog”
    • red|blue|以及red||blue以及|red|blue都表示匹配red或者blue或者一个空字符串
    • a|b|c与[abc]相同
    • cat|dog||表示匹配”cat”或者”dog”或者一个分隔符”|“
    • [cat|dog]表示匹配a或者c或者d或者g或者o或者t或者一个分隔符“|”

    练习

    简化下列正则表达式:

    • s|t|u|v|w
    • aa|ab|ba|bb
    • [abc]|[^abc]
    • [^ab]|[^bc]
    • [ab][ab][ab]?[ab]?

    答案

    • [s-w]
    • [ab]{2}
    • .
    • [^b]
    • [ab]{2,4}

    练习

    使用正则表达式匹配1到31之间的整数,[1-31]不是正确答案!

    这样的正则表达式不唯一. [1-9]|[12][0-9]|3[01] 是其中之一。

    分组

    你可以使用括号表示分组:

    • 通过使用 Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day 匹配一周中的某一天
    • (\w*)ility 与 \w*ility 相同。都是匹配一个由”ility”结尾的单词。稍后我们会讲解,为何第一种方法更加有用。
    • [()]表示匹配任意一个左括号或者一个右括号

    分组可以包括空字符串:

    • (red|blue)表示匹配red或者blue或者是一个空字符串
    • abc()def与abcdef相同

    你也可以在分组的基础上使用重复:

    • (red|blue)?与(red|blue|)相同
    • \w+(\s+\w+)表示匹配一个或多个由空格分隔的单词

    练习

    简化正则表达式 \w+\W+\w+\W+\w+ 以及 \w+\W+\w+\W+\w+\W+\w+\W+\w+\W+\w+.

    答案

    \w+(\W+\w+){2}, \w+(\W+\w+){5}.

    单词分隔符

    在单词和非单词之间有单词分隔符。记住,一个单词\w是[0-9A-Za-z_],而非单词字符是\W(大写),表示[^0-9A-Za-z_].

    在文本的开头和结尾通常也有单词分隔符。

    在输入文本it’s a cat中,实际有八个单词分隔符。如果我们在cat之后在上一个空格,那就有九个单词分隔符。.

    • \b表示匹配一个单词分隔符
    • \b\w\w\w\b表示匹配一个三字母单词
    • a\ba表示匹配两个a中间有一个单词分隔符。这个正则表达式永远不会有匹配的字符,无论输入怎样的文本。

    单词分隔符本身并不是字符。它们的宽度为0。下列正则表达式的作用不同

    • (\bcat)\b
    • (\bcat\b)
    • \b(cat)\b
    • \b(cat\b)

    练习

    在词典中找到最长的单词。

    答案

    在尝试之后发现,\b.{45,}\b可以在字典中找到最长单词

    换行符

    一篇文本中可以有一行或多行,行与行之间由换行符分隔,比如:

    • Line一行文字
    • Line break换行符
    • Line一行文字
    • Line break换行符
    • Line break换行符
    • Line一行文字

    注意,所有的文本都是以一行结束的,而不是以换行符结束。但是,任意一行都可能为空,包括最后一行。

    行的起始位置,是在换行符和下一行首字符之间的空间。考虑到单词分隔符,文本的起始位置也可以当做是首行位置。

    最后一行是最后一行的尾字符和换行符之间的空间。考虑到单词分隔符,文本的结束也可以认为是行的结束。

    那么新的格式表示如下:

    • Start-of-line, line, end-of-line
    • Line break
    • Start-of-line, line, end-of-line
    • Line break
    • Line break
    • Start-of-line, line, end-of-line

    基于上述概念:

    • ^表示匹配行的开始位置
    • $表示匹配行的结束位置
    • ^&表示一个空行
    • ^.*&`` 表示匹配全文内容,因为行的开始符号也是一个字符,"."会匹配这个符号。找到单独的一行,可以使用 ^.*?$
    • \^$表示匹配字符串“^$”
    • [ ] 。但是,[^]不是合法的正则表达式。记住在方括号中,字符有不同的特殊含义。要想在方括号内匹配^,必须用[\^]

    与字符分隔符一样,换行符也不是字符。它们宽度为0.如下所示的正则表达式作用不同:

    • (^cat)$
    • (^cat$)
    • ^(cat)$
    • ^(cat$)

    练习

    使用正则表达式在《时间机器》中找到最长的一行。

    答案

    使用正则表达式^.{73,}$可以匹配长度为73的一行

    文本分界

    在很多的正则表达式实现中,将^和$作为文本的开始符号和结束符号。

    还有一些实现中,用\A和\z作为文本的开始和结束符号。

    捕捉和替换

    从这里开始,正则表达式真正体现出了它的强大。

    捕获组

    你已经知道了使用括号可以匹配一组符号。使用括号也可以捕获子串。假设正则表达式是一个小型计算机程序,那么捕获子串就是它输出的一部分。

    正则表达式(\w*)ility表示匹配以ility结尾的词。第一个被捕获的部分是由\w*控制的。比如,输入的文本内容中有单词accessibility,那么首先被捕获的部分是accessib。如果输入的文本中有单独的ility,则首先被捕获的是一个空字符串。

    你可能会有很多的捕获字符串,它们可能靠得很近。捕获组从左向右编号。也就是只需要对左括号计数。

    假设有这样的正则表达式:(\w+) had a ((\w+) \w+)

    输入的内容是:I had a nice day

    • 捕获组1:I
    • 捕获组2:nice day
    • 捕获组3:nice

    在一些正则表达式的实现中,你可以从零开始编号,编号零表示匹配整句话:I had a nice day.

    在其他的实现中,如果没有制定捕获组,那么捕获组1会自动地填入捕获组0的信息。

    是的,这也意味着会有很多的括号。有一些正则表达式的实现中,提供了“非捕获组”的语法,但是这样的语法并不是标准语法,因此我们不会介绍。

    从一个成功的匹配中返回的捕获组个数,与使用原来的正则表达式获得的捕获组个数相同。记住这一点,你可以解释一些奇怪的现象。.

    正则表达式((cat)|dog)表示匹配cat或者dog。这里有两个捕获组,如果输入文本是dog,那么捕获组1是dog,捕获组2为空。

    正则表达式a(\w)*表示匹配一个以a开头的单词。这里只有一个捕获组

    • 如果输入文本为a,捕获组1为空。
    • 如果输入文本为ad,捕获组为d
    • 如果输入文本为avocado,捕获组1为v。但是捕获组0表示整个单词avocado.

    替换

    假如你使用了一个正则表达式去匹配字符串,你可以描述另外一个字符串来替换其中的匹配字符。用来替换的字符串称为替换表达式。它的功能类似于

    • 常规的Replace会话
    • Java中的String.replace()函数
    • PHP的str_replace()函数
    • 等等

    练习

    将《时间机器》中所有的元音字母替换为r。

    答案

    使用正则表达式[aeiou]以及[AEIOU],对应的替换字符串分别为r,R.

    但是,你可以在替换表达式中引用捕获组。这是在替换表达式中,你可以唯一操作的地方。这也是非常有效的,因为这样你就不用重构你找到的字符串。

    假设你正在尝试将美国风格的日期表示MM/DD/YY替换为ISO 8601日期表示YYYY-MM-DD

    • 从正则表达式(\d\d)/(\d\d)/(\d\d)开始。注意,这其中有三个捕获组:月份,日期和两位的年份。

    • .捕获组的内容和捕获组编号之间用反斜杠分隔,因此你的替换表达式应该是20\3-\1-\2.

    • 如果我们输入的文本中包含03/04/05表示2005年3月4日那么:
      • 捕获组1:03
      • 捕获组2:04
      • 捕获组3:05
      • 替换字符串2005-03-04.

    在替换表达式中,你可以多次使用捕获组

    • 对于双元音,正则表达式为([aeiou]),替换表达式为\l\l
    • 在替换表达式中不能使用反斜杠。比如,你在计算机程序中希望使用字符串中使用部分文本。那么,你必须在每个双引号或者反斜杠之前加上反斜杠。

    • 你的正则表达式可以是([\”])。捕获组1是双引号或者反斜杠

    • 你的替换表达式应该是\\l

    在某些实现中,采用美元符号$代替\

    练习

    使用正则表达式和替换表达式,将23h59这样的时间戳转化为23:59.

    答案

    正则表达式finds the timestamps, 替换表达式\1:\2

    反向引用

    在一个正则表达式中,你也可以引用捕获组。这称作:反向引用

    比如,[abc]{2}表示匹配aa或者ab或者ac或者ba或者bb或者bc或者ca或者cb或者cc.但是{[abc]}\1表示只匹配aa或者bb或者cc.

    练习

    在字典中,找到包含两次重复子串的最长单词,比如papa, coco

    \b(.{6,})\1\b 匹配 chiquichiqui.

    如果我们不在乎单词的完整性,我们可以忽略单词的分解,使用正则表达式 (.{7,})\1``匹配``countercountermeasure 以及 countercountermeasures.

    使用正则表达式编程

    特别提醒:

    过度使用的反斜杠

    在一些编程语言,比如Java中,对于包含正则表达式的字符串没有特殊标记。字符串有着自己的过滤规则,这是优先于正则表达式规则的,这是频繁使用反斜杠的原因。

    比如在Java中

    • 匹配一个数字,使用的正则表达式从\d变为代码中的String re= “\d”
    • 匹配双引号字符串的正则表达式从"[^"]*" 变为String re = “\”[^\”]*\””
    • String re = "\\s";String re = "[ \t\r\n]"; 是等价的. 注意它们实际执行调用时的层次不同。

    在其他的编程语言中,正则表达式是由特殊标明的,比如使用/。下面是JavaScript的例子:

    • 匹配一个数字,\d会简单写成 var regExp = /\d/;.
    • 匹配一个反斜杠或者一个左边的方括号或者一个右边的方括号, var regExp = /[\\\[\]]/;
    • var regExp = /\s/;var regExp = /[ \t\r\n]/; 是等价的
    • 当然,这意味着在使用/时必须重复两次。比如找到URL必须使用var regExp = /https?:\/\//;.

    我希望现在你能明白,我为什么让你特别注意反斜杠。

    动态正则表达式

    当你动态创建一个正则表达式的时候请特别小心。如果你使用的字符串不够完善的花,可能会有意想不到的匹配结果。这可能导致语法错误,更糟糕的是,你的正则表达式语法正确,但是结果无法预料。

    错误的Java代码:

    String sep = System.getProperty(“file.separator”); String[] directories = filePath.split(sep);

    Bug:String.split() 认为sep是一个正则表达式。但是,在Windows中,Sep是表示匹配一个反斜杠,也就是与正则表达式”\”相同。这个正则表达式是正确的,但是会返回一个异常:PatternSyntaxException.

    任何好的编程语言都会提供一种良好的机制来跳过字符串中所有的元字符。在Java中,你可以这样实现:

    String sep = System.getProperty(“file.separator”);

    String[] directories = filePath.split(Pattern.quote(sep));

    循环中的正则表达式

    将正则表达式字符串加入反复运行的程序中,是一种开销很大的操作。如果你可以在循环中避免使用正则表达式,你可以大大提高效率。

    其他建议

    输入验证

    正则表达式可以用来进行输入验证。但是严格的输入验证会使得用户体验较差。比如:

    信用卡号

    在一个网站上,我输入了我的卡号比如 1234 5678 8765 4321 ``网站拒绝接收。因为它使用了正则表达式\d{16}。

    正则表达式应该考虑到用户输入的空格和短横线。

    实际上,为什么不先过滤掉所有的非数字字符,然后再进行有效性验证呢?这样做,可以先使用\D以及空的替换表达式。

    练习

    在不先过滤掉所有的非数字字符的情况下,使用正则表达式验证卡号的正确性。

    答案

    \D*(\d\D*){16} is one of several variations which would accomplish this.

    名字

    不要使用正则表达式来验证姓名。实际上,即使可以,也不要企图验证姓名。

    程序员对名字的错误看法:

    • 名字中不含空格
    • 名字中没有连接符号
    • 名字中只会使用ASCII码字符
    • 名字中出现的字都在特殊字符集中
    • 名字至少要有M个字的长度
    • 名字不会超过N个字的长度
    • 人们只有一个名
    • 人们只有一个中间名
    • 人们只有一个姓(最后三条是从英语的人名考虑)

    电子邮件地址

    不要使用正则表达式验证邮箱地址的正确性。

    首先,这样的验证很难是精确的。电子邮件地址是可以用正则表达式验证的,但是表达式会非常的长并且复杂。

    短的正则表达式会导致错误。(你知道吗?电子邮箱地址中会有一些注释)

    第二,即使一个电子邮件地址可以成功匹配正则表达式,也不代表这个邮箱实际存在。邮箱的唯一验证方法,是发送验证邮件。

    注意

    在严格的应用场景中,不要使用正则表达式来解析HTML或者XML。解析HTML或者XML:

    1. 使用简单的正则表达式不能完成
    2. 总体来说非常困难
    3. 已经有其他的方法解决

    找到一个已经有的解析库来完成这个工作

    总结

    • 字符: a b c d 1 2 3 4 etc.

    • 字符类:

      .
      [abc]
      [a-z]
      \d
      \w
      \s
      • . 代表任何字符
      • \d ``表示“数字”
      • \w 表示”字母”, [0-9A-Za-z_]
      • \s 表示 “空格, 制表符,回车或换行符”
      • 否定字符类: [^abc] \D \W \S
    • 重复:

      {4}
      {3,16}
      {1,}
      ?
      *
      +
      • ? 表示 “零次或一次”
      • * 表示 “大于零次”
      • + 表示 “一次或一次以上”
      • 如果不加上?,所有的重复都是最长匹配的(贪婪)
    • 分组: (Septem|Octo|Novem|Decem)ber

    • 词,行以及文本的分隔: \b ^ $ \A \z

    • 转义字符: \1 \2 \3 etc. (在匹配表达式和替换表达式中都可用)

    • 元字符: . \ [ ] { } ? * + | ( ) ^ $

    • 在字符类中使用元字符: [ ] \ - ^
    • 使用反斜杠可以忽略元字符: \
    展开全文
  • 常用正则表达式

    2017-06-20 13:11:13
    网上找到的常用正则表达式,留着以后可能用得上,正则表达式实在是不好写,只好拿来主义了,在Delphi中没有自己带有正则表达式的组件,靠第三方了,都说PerlRegEx 是首选, 去这里下载,官方网站: ...

    网上找到的常用正则表达式,留着以后可能用得上,正则表达式实在是不好写,只好拿来主义了,在Delphi中没有自己带有正则表达式的组件,靠第三方了,都说PerlRegEx 是首选, 去这里下载,官方网站: http://www.regular-expressions.info/delphi.html。另外,万一老师在他的博客上写了PerRegEx的使用,可以去看看:http://www.cnblogs.com/del/archive/2007/12/21/1008108.html

    以下是转载的文章,有了现成的表达式,又有现成的正则表达式类,应该可以满足一般需求了,呵呵。

     

     

     

    常用正则表达式大全!(例如:匹配中文、匹配html)

     匹配中文字符的正则表达式: [u4e00-u9fa5]  
      评注:匹配中文还真是个头疼的事,有了这个表达式就好办了
      匹配双字节字符(包括汉字在内):[^x00-xff]
      评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
      匹配空白行的正则表达式:ns*r
      评注:可以用来删除空白行
      匹配HTML标记的正则表达式:<(S*?)[^>]*>.*?|<.*? />
      评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力
      匹配首尾空白字符的正则表达式:^s*|s* ()  Emailw+([+.]w+)@w+([.]w+).w+([.]w+)    URL[azAz]+://[s]    (516线)[azAZ][azAZ09]4,15
      评注:表单验证时很实用
      匹配国内电话号码:d{3}-d{8}|d{4}-d{7}
      评注:匹配形式如 0511-4405222 或 021-87888822
      匹配腾讯QQ号:[1-9][0-9]{4,}
      评注:腾讯QQ号从10000开始
      匹配中国邮政编码:[1-9]d{5}(?!d)
      评注:中国邮政编码为6位数字
      匹配身份证:d{15}|d{18}
      评注:中国的身份证为15位或18位
      匹配ip地址:d+.d+.d+.d+
      评注:提取ip地址时有用
      匹配特定数字:
      ^[1-9]d* //  [19]d   //匹配负整数
      ^-?[1-9]d* //  [19]d|0   //匹配非负整数(正整数 + 0)
      ^-[1-9]d*|0 //+0  [19]d.d|0.d[19]d    //匹配正浮点数
      ^-([1-9]d*.d*|0.d*[1-9]d*) //  ?([19]d.d|0.d[19]d|0?.0+|0)   //匹配浮点数
      ^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0 //+0  (([19]d.d|0.d[19]d))|0?.0+|0   //匹配非正浮点数(负浮点数 + 0)
      评注:处理大量数据时有用,具体应用时注意修正
      匹配特定字符串:
      ^[A-Za-z]+ //26  [AZ]+   //匹配由26个英文字母的大写组成的字符串
      ^[a-z]+ //26  [AZaz09]+   //匹配由数字和26个英文字母组成的字符串
      ^w+ //26线  使RegularExpressionValidator:  [09]
      只能输入n位的数字:“^d{n}   ndn,
      只能输入m-n位的数字:“^d{m,n}   (0|[19][09])
      只能输入有两位小数的正实数:“^[0-9]+(.[0-9]{2})?   13[09]+(.[09]1,3)?
      只能输入非零的正整数:“^+?[1-9][0-9]*   [19][09]
      只能输入长度为3的字符:“^.{3}   26[AZaz]+
      只能输入由26个大写英文字母组成的字符串:“^[A-Z]+   26[az]+
      只能输入由数字和26个英文字母组成的字符串:“^[A-Za-z0-9]+   26线w+
      验证用户密码:“^[a-zA-Z]w{5,17}”正确格式为:以字母开头,长度在6-18之间,  
      只能包含字符、数字和下划线。  
      验证是否含有^%&”,;=?
    "等字符:“[^%&amp;'',;=? x22]+  [u4e00u9fa5],0,
      验证Email地址:“^w+[-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*   InternetURLhttp://([w]+.)+[w]+(/[w./?
      验证电话号码:“^((d{3,4})|d{3,4}-)?d{7,8}   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXXXXXXXXXXX  1518d15|d18
      验证一年的12个月:“^(0?[1-9]|1[0-2]) 0109112  31((0?[19])|((1|2)[09])|30|31)
      正确格式为:“01”“09”和“1”“31”。
      匹配中文字符的正则表达式: [u4e00-u9fa5]
      匹配双字节字符(包括汉字在内):[^x00-xff]
      匹配空行的正则表达式:n[s| ]*r
      匹配HTML标记的正则表达式:/<(.*)>.*|<(.*) />/
      匹配首尾空格的正则表达式:(^s*)|(s*$)
      匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
      匹配网址URL的正则表达式:http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?
      (1)应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
      String.prototype.len=function(){return this.replace([^x00-xff]/g,”aa”).length;}
      (2)应用:JavaScript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现
      String.prototype.trim = function()
      {
      return this.replace(/(^s*)|(s*)/g, “”);  
      }  
      (3)应用:利用正则表达式分解和转换IP地址  
      function IP2V(ip) //IP地址转换成对应数值  
      {  
      re=/(d+).(d+).(d+).(d+)/g //匹配IP地址的正则表达式  
      if(re.test(ip))  
      {  
      return RegExp.
    1*Math.pow(255,3))+RegExp. 2Math.pow(255,2))+RegExp. 3*255+RegExp.$4*1
      }
      else
      {
      throw new Error(“Not a valid IP address!”)
      }
      }
      (4)应用:从URL地址中提取文件名的javascript程序
      s=”http://www.9499.net/page1.htm“;
      s=s.replace(/(.*/){0,}([^.]+).*/ig,”$2”) ;//Page1.htm
      (5)应用:利用正则表达式限制网页表单里的文本框输入内容
      用正则表达式限制只能输入中文:οnkeyup=”value=value.replace(/[^u4E00-u9FA5]/g,”) “onbeforepaste=”clipboardData.setData(”text”,clipboardData.getData(”text”).replace(/[^u4E00-u9FA5]/g,”))”
      用正则表达式限制只能输入全角字符: οnkeyup=”value=value.replace(/[^uFF00-uFFFF]/g,”) “onbeforepaste=”clipboardData.setData(”text”,clipboardData.getData(”text”).replace(/[^uFF00-uFFFF]/g,”))”
      用正则表达式限制只能输入数字:οnkeyup=”value=value.replace(/[^d]/g,”) “onbeforepaste= “clipboardData.setData(”text”,clipboardData.getData(”text”).replace(/[^d]/g,”))”
      用正则表达式限制只能输入数字和英文:οnkeyup=”value=value.replace(/[W]/g,”) “onbeforepaste=”clipboardData.setData(”text”,clipboardData.getData(”text”).replace(/[^d]/g,”

    展开全文
  • java 正则表达式

    2014-05-30 22:31:39
    本文学习并总结java中正则表达式语法及其具体功能。使用正则表达式可以非常方便灵活地操作字符串,在实际项目开发中也很实用。 正则表达式 符合一定规则的表达式
  • 正则表达式一 基本正则表达式1.字符匹配1.匹配次数三级目录 写在前面:一直以来 对于正则表达式的使用都没有进行一个系统性的梳理学习,导致在工作中进行脚本编写时,总是会出现一些不符合自己认知的错误,最近抽空...
  • fmpeg中表达式

    2017-07-12 15:36:24
    在FFmpeg可以对算术表达式求值。FFmpeg有一套内部的表达式规则,通过libavutil/eval.h提供的接口实现。表达式类型包含一元、二元、常量和函数。 两个表达式expr1、expr2可以通过分号组合成为一个新的表达式"expr1...
  • 在这篇教程中,你将学习如何在 iOS app 中使用 Swift 4.2 实现正则表达式。 更新说明:Tom Elliott 将本教程升级至 Swift 4.2。原文作者是 James Frost。 正则表达式基础 如果你没有听过正则表达式——也...
  • 关于正则表达式

    2017-05-22 16:14:37
    正则表达式 编辑 正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。 许多程序...
  • 正则表达式的概念 正则表达式(英语:Regular Expression,在代码中常简写为regex)。 正则表达式是一个字符串,使用单个字符串来描述、用来定义匹配规则,匹配一系列符合某个句法规则的字符串。在开发中,正则...
  • 正则表达式大全

    万次阅读 2019-07-19 10:11:11
    评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 匹配空白行的正则表达式:nsr 评注:可以用来删除空白行 匹配HTML标记的正则表达式:< (S?)[^>]>.?|< .? /&g...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,950
精华内容 10,780
关键字:

整则表达式长度