开发之正则表达式的语法详解

ygj201011 8年前
   <h3><strong>正则表达式</strong></h3>    <p><strong>序言</strong></p>    <p>好久没写过文章啦,距离自己上一次的记录已经过去了半年多了,也许是工作忙了,也许是自己懒惰了,也许是...最近的工作不怎么忙,心就想着要写点什么呢,由于同事的几句话,真的"机缘巧合"的写了这个知识,虽然这个知识点有点陈旧哈,但我相信对于那些对正则刚入门初学的开发者来说,你认真看过这篇文章之后,对于正则的认识、语法规则必会有一个大大提升的,若再附加自己的实践,相信你对于掌握好这个知识点是没有什么问题的哈,:grin:</p>    <p><strong>含义</strong></p>    <p>是由普通字符(字符 a 到 z)以及特殊字符("元字符")组成的文字模式(即字符串文本的复杂处理(查找、匹配、验证))</p>    <p><strong>核心</strong></p>    <p>正则表达式的语法规则</p>    <p><strong>原则</strong></p>    <p>尽可能罗列出所有出现的情况,然后再按照罗列的字符串写出符合要求的正则表达式,尽可能的精确匹配</p>    <p><strong>建议</strong></p>    <p>虽然现在常见的正则表达式我们都可以查到,但还是应该对正则语法有一些认识,不可生搬硬套别人的正则表达式</p>    <p><strong>普通字符</strong></p>    <p>包括没显式指定为元字符的所有可打印和不可打印字符(所有大写和小写字母、所有数字、所有标点符号和一些其他符号)</p>    <p><strong>特殊字符</strong></p>    <p>若匹配这些特殊字符,须先使用转义字符``放在它们前面</p>    <p><img src="https://simg.open-open.com/show/ae2188b98a4057cd3f03c256928b3087.png"></p>    <p><strong>几点说明</strong></p>    <ul>     <li>匹配包括 '\n' 在内的任何单字符 - [.\n]</li>     <li>选择符 | 一般结合非捕获元字符 ?: 和分组 () 搭配使用,因为它的优先级最低</li>     <li>分组 () 是对于复杂的文本分割成子表达式使用,一般结合反向引用 \n 使用,这时编号是按照左括号往右的一次叠加的,即:编号从 1 开始,最多可存储 99 个捕获的子表达式,场景: 匹配重复出现的字符串</li>     <li>中括号表达式(字符簇) [] ,表示所有字符的字符簇(集合),若在中括号表达式中包括连字符 - ,则需要用反斜扛将它转义,但是若将连字符放在中括号列表的开始或结尾,则不需反斜扛转义</li>    </ul>    <p><strong>非打印字符</strong></p>    <p>下图列出了非打印字符的转义序列</p>    <p><img src="https://simg.open-open.com/show/54d8586f425be49a7f24f9a908f55666.png"></p>    <p><strong>限定符</strong></p>    <p>限定符用来指定正则的一个子表达式出现多少次能满足匹配,共6种</p>    <p><img src="https://simg.open-open.com/show/853a811a7940e3b59c96aebdc4695bd9.png"></p>    <p><strong>几点说明</strong></p>    <ul>     <li>限定符 * 、 + 、 ? 都是贪婪的,它们会尽可能多的匹配所搜索的字符串,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配</li>     <li>即: ? 放在任何一个其他限定符 ( * , + , ? , {n} , {n,} , {n,m} ) 后面时,匹配模式是非贪婪的,非贪婪模式会尽可能少的匹配所搜索的字符串(比如:对于字符串 "chongzoneeeee",'e+?' 将匹配单个 "e",而 'e+' 将匹配所有 'e'),场景:检测字符串是否包含某个字符等</li>    </ul>    <p><strong>定位符</strong></p>    <p>定位符使您能够将正则固定到行首或行尾</p>    <p><img src="https://simg.open-open.com/show/1a65ece6173a5934d854cbf3fbef2379.png"></p>    <p><strong>注意点</strong></p>    <p>不能将限定符与定位符一起使用,因为在紧靠换行或字边界的前面或后面不能有一个以上位置</p>    <p><strong>预搜索(零宽断言assert)</strong></p>    <p>零宽断言是对位置的匹配,共4种(下图列出了常见的2种)</p>    <p><img src="https://simg.open-open.com/show/4dc9946bd4737ff8368808a11c91fa70.png"></p>    <p><strong>几点说明</strong></p>    <ul>     <li>(?:pattern) - 分组虽然使匹配更加清晰,但同时会产生一个副作用,使相关的匹配被缓存(存储到一个临时缓冲区中),此时使用 ?: 放在分组选项前可消除这种副作用,不捕获匹配的文本,即:不给分组分配组号,则此时对于重复的字符串就不能使用反向引用</li>     <li>(?=pattern) - 匹配以pattern结尾的前面的部分字符串,不包括pattern部分</li>     <li>(?<=pattern) - 匹配以pattern开头的后面的部分字符串,不包括pattern部分</li>     <li>(?!pattern) - 断言位置的后面不能匹配表达式pattern</li>     <li>(?<!pattern) - 断言位置的前面不能匹配表达式pattern</li>    </ul>    <p>即: (?=pattern) 和 (?!pattern) 放在后面, (?<=pattern) 和 (?<!pattern) 放在前面,场景: 取出需要的字符串</p>    <p><strong>反向引用</strong></p>    <p>反向引用用于重复搜索前面某个分组匹配的子表达式,当然也可以自己指定子表达式的组名</p>    <p><strong>语法规则</strong></p>    <p>(?<name>\w+)(或':(?'name'\w+)) ,这样组名\w+的就定为 name ,若是反向引用这个分组,则使用 \k<name> ,即: \b(?<name>\w+)\b\s+\k<name>\b</p>    <p><strong>其他元字符</strong></p>    <p><img src="https://simg.open-open.com/show/d58922121789199d86a996280b32ffd6.png"></p>    <p><img src="https://simg.open-open.com/show/917708dd08f18d3e161be260470e945b.png"></p>    <p><img src="https://simg.open-open.com/show/49b72d56afde16186375ebf6851c7742.png"></p>    <p><strong>不常用的其他元字符</strong></p>    <ul>     <li>模式修改符 - (?i) 开 和 (?-i) 关 - 包含区域不区分大小写 - 不建议使用</li>     <li>注释 - (?#comment) ,对于不熟悉的匹配规则可以使用</li>    </ul>    <p><strong>运算符的优先级</strong></p>    <p>下表从上到下列出了优先级最高到最低的排序</p>    <p><img src="https://simg.open-open.com/show/63021f220228b176bf6aeb52149ba873.png"></p>    <h3><strong>常用的正则表达式</strong></h3>    <p>写到这里的时候,自己呢本是不想写这个常用正则表达式部分的,但基于我看到的其他开发者写的或归纳整理的正则表达式,大部分都是COPY下来的,里面的有些正则表达式明显是有错误的...这样传阅下来估计会误导不少初学者呢,虽然对客户端开发者来说,正则使用的不是很频繁,在开发中用到的也就那么几种,现在的互联网这么发达,百度谷歌下不就知道啦,当然了还有另一个原因,由于正则的语法比较繁琐,时间一长不经常使用的话难免有些正则会被遗忘的,这样整理的话自己以后使用也是很方便的,好记性比不上烂笔头嘛,哈哈,废话就不多说了,有什么写的不对的地方请多多交流哈��(ノへ ̄、)</p>    <p>1.用户名(首字母,长度6-16)</p>    <pre>  "^[a-zA-Z]\\w{5,15}$"</pre>    <p>2.表单密码(长度6-16)</p>    <pre>  "^\\.{6,16}$"</pre>    <p>3.验证码(长度6)</p>    <pre>  "^\\d{6}$"</pre>    <p>4.固话号码(首数字0)</p>    <pre>  "^0\\d{2,3}-\\d{7,8}$"</pre>    <p>5.手机号码(长度11)</p>    <pre>  "1|[34578]\\d{9}"</pre>    <p>6.身份证号(长度18,长度15已淘汰)</p>    <pre>  "^\\d{17}[\\dxX]$"</pre>    <p>7.Email地址(.com、.cn等)</p>    <pre>  "^\\w+@\\w+(\\.[a-zA-Z]{2,3}){1,2}$"</pre>    <p>8.仅含数字</p>    <pre>  "^\\d+$”</pre>    <p>9.仅含字母</p>    <pre>  "^[a-zA-Z]+$"</pre>    <p>10.仅含数字和字母</p>    <pre>  "^[0-9a-zA-Z]+$"</pre>    <p>11.整数</p>    <pre>  "^\\-?\\d+$"</pre>    <p>12.小数</p>    <pre>  "^\\d+\\.\\d*$"</pre>    <p>13.浮点数</p>    <pre>  "^\\-?\\d+\\.?\\d*$"</pre>    <p>14.正整数(最小1)</p>    <pre>  "^\\+?[1-9]+$"</pre>    <p>15.负整数(最大-1)</p>    <pre>  "^\\-[1-9]+$"</pre>    <p>16.n位的小数</p>    <pre>  "^\\d+\\.\\d{n}$"</pre>    <p>17.月份(长度1\2)</p>    <pre>  "^(0?[1-9]|1[0-2])$"</pre>    <p>18.日数(最大31)</p>    <pre>  "^((0?[1-9])|((1|2)[0-9])|30|31)$"</pre>    <p>19.含空白行</p>    <pre>  "\\s*"</pre>    <p>20.首位空白符</p>    <pre>  "^\\s+|\\s+$"</pre>    <p>21.含双字节</p>    <pre>  "[^\x00-\xff]"</pre>    <p>22.包含中文(检测工具不支持)</p>    <pre>  "[\\u4e00-\\u9fa5]+"</pre>    <p>23.搜索叠词</p>    <pre>  "\\b([a-z]+) \1\\b"</pre>    <p>24.含n个字母的单词</p>    <pre>  "[[:alpha:]]{n}"</pre>    <p>25.中国邮编(长度6)</p>    <pre>  "^[1-9]\\d{5}$"</pre>    <p>26.QQ号码(最小5位,目前最大10位)</p>    <pre>  "^[1-9]\\d{5}$"</pre>    <p>27.HTML标签</p>    <pre>  "<\\s*(\\S+)(\\s[^>]*)?>[\\s\\S]*<\\s*\\/\\1\\s*>"</pre>    <p>28.网络URL(附带端口号匹配)</p>    <pre>  "http(s?):\\/\\/([^\\/:]+)(:\\d*)?([^ ]*)"</pre>    <p>29.网页图片</p>    <pre>  "\\< *[img][^\\\\>]*[src] *= *[\\"\\']{0,1}([^\\"\\'\\ >]*)"</pre>    <p>30.IP地址</p>    <pre>  "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"</pre>    <h3><strong>iOS开发中的使用</strong></h3>    <p>在iOS开发中, <strong>Cocoa框架</strong> 给我们提供了谓词 NSPredicate 类,类似过滤器的作用,就相当于 SQL 的 where 条件,在iOS开发中,我们常用的做法将谓词和正则表达式配合使用,代码如下:</p>    <pre>  /**   * 匹配字符串   *   @param inputText 输入的文本   @param validRegular 正则表达式   @return YES 匹配成功 NO 匹配失败   */  - (BOOL)verifysInputText:(NSString *)inputText validRegular:(NSString *)validRegular {      NSPredicate *predicte = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",validRegular];      return [predicte evaluateWithObject:inputText];  }</pre>    <h3><strong>结束语</strong></h3>    <p>最后呢,推荐给大家一个在线的正则表达式检测工具 <strong> Regex101 </strong> ,当然在Mac端,你也可以下载一个客户端 <strong>RegExRx</strong> ,不过它不是免费的,需要30块大洋,但它的界面简洁,为了学习嘛,都懂的,当然你也可以下载个破解版...:grin:</p>    <p>(ps:在检测工具内,单斜杠 不需要转为双斜杠 \ ,因为只有在代码中,编译器是默认单斜杠为转义字符,需要你转为双斜杠,所以上面你 看到的代码全是双斜杠的 \`,还有就是基本常见的检测工具都是不支持检测中文输入的,因为不能识别 "\u" 字符)</p>    <p>对于这个知识点、文章有什么不太理解的地方,还请在下面留言,看到之后有时间会回复的... 午安。。。</p>    <p><img src="https://simg.open-open.com/show/d74688278feb2ec7d2dab5039a10905e.png"></p>    <p><strong>Regex101测试工具</strong></p>    <p><img src="https://simg.open-open.com/show/5da511c079ee4e4b7e68576eaaccd301.png"></p>    <p>RegExRx测试工具</p>    <p> </p>    <p>来自:https://juejin.im/entry/58ca26c3570c3500588ec9a6</p>    <p> </p>