华杰简易入门系列之正则表达式——基础篇

话说最近教主立了一个flag,说不女装就写博客,啊,不对,是不写博客就女装,我想着反正也没什么事情也写一篇玩玩,当作激励一下他女装的动力<_<

其实网上关于正则表达式的教程已经够多了,什么各种30分钟入门啊,简易语法啊比比皆是,如果你用某不存在的搜索引擎或者随便百度一下就能找到数不胜数的教程。既然如此,我为什么要写这篇文章呢?第一点前面已经说了,其次是由于现在的教程都过于复杂,其实不利于入门,就如我说的那篇《正则表达式30分钟入门教程》不可否认作者写的非常翔实,而且也很不错,我个人也很喜欢,但作为一个初学者看到这么多的内容第一反应就是望而生畏,接着就放弃了。这很正常,就和我看英文文档时一样。所以我觉得应该写一篇比较简单易懂的教程出来,正所谓骗一个是一个。

其实说了许多,我想还是会有人问为什么要学正则表达式?至于这个问题吧,其实见仁见智,记得囧乎上有人问过《为什么大家都不喜欢用正则表达式?》,其实在我看来你纵有千种理由,其实说白了就一个字————懒。
毕竟世界这么大,工作这么忙,能动动手指就找到的东西为何还要花心思去记呢?如果你也是这么认为的,那么估计本教程不适合你,请点击右上角。如果你愿意去花费一定的时间来看这篇小文,那么我在此先谢过了。

另外这篇文章既然是面向初学者的,所以我也尽量使文章保持简单易读,同时文风轻松明快一些,努力做到即使你不是做技术的也能很快看懂。

最后在开始之前我想说,请一定要静下心来,戒骄戒躁,非宁静无以致远。

从零开始

其实说到正则表达式我们并不陌生,可以说只要你用电脑几乎都用过。好吧,也许你说你没有电脑,那你总有手机吧,万一你说手机也没有,那好歹还有眼耳口鼻吧,你总看过听过闻过尝过感触过这个花花世界吧,如果你说这也没有,那么好吧,你赢了。顺带地球很危险,so……

你或许会问了,这些和正则有什么关系呢?答:没有关系<_< 好吧,原谅我稍微夸大了一下,简单说所谓的正则说白了就是模式匹配,在计算机里面主要就是字符串的匹配。现在看到了吧,其实还是有点关系的,你之所以认为苹果手机是手机,老式大哥大也是手机,是不是在大脑中有一个匹配的过程?他们或许长得很不同,但你之所以认为他们都是手机是不是因为某些特定的特征或者联系?这就是正则匹配的思想。

现在让我们回到电脑上。当我们在电脑上工作或学习的时候总会碰到需要搜索的情况,比如说在这篇文章中想找"正则表达式"这五个字,然后再把它替换成"Regular Expression",我想你一定很习惯了,不就是ctrl+F 和 ctrl+H吗?但如果我希望只将书名号里面的"正则表达式"替换掉呢,该怎么办?一个个找?那万一像指环王那么厚的一套书岂不是关这一项工作就要干几天几夜?万一你改完第二天老大突然说:还是改回去吧,中英混合不好看,估计万匹草泥马在奔腾。

所以学了这篇文章之后你至少可以把这中工作几秒钟就完成了,然后可以舒舒服服的喝杯咖啡,撩撩妹子,何乐而不为呢。

基础篇之字符

首先你需要记住一点,字符永远是正则表达式的一等公民。所以正则表达式永远是匹配一个字符!这句话很重要,所以请一定要记住(至于环视,你可以理解为匹配一个宽度是0的字符,所以就变成匹配位置了,当然这会在后面讲到)。为了便于理解,请你打开任意一个你的windows窗口,你会发现右上角有一个搜索框,没错,我们的试验就从这里开始。
如果你经常使用的话会应该会知道windows的搜索是支持通配符的,比如?号代表一个字符,*号代表0到多个字符。这个的最大好处就是我们可以少打几个字,又或是找到包含相同字符的一组文件。请仔细测试一下,看看返回的数据究竟有什么区别。当然如果你用的是新版的windows的话,额,嗯,不用看了,这两个基本上没什么太大作用。。。

现在你基本上应该有了一个比较感性的认识————哈?做不了试验怪我咯?
这里我们可以确定几点,首先你可以使用任意字符来搜索你想要的文件,除了那两个;其次根据你选择的通配符不同你得到的结果也不尽相同,第三通配符是一组有特殊意义的标点符号。

OK,这三点也和正则表达式相同。正则表达式使用的字符没有任何限制,但其中部分的标点符号被给予了特殊的意义,并且通过将这些标点符号与普通字符的组合可以得到不同结果的匹配。

不过有一点需要注意,正则表达式是区分大小写的,比如Lucky和lucky在正则中是不一样的。

其中我们将那些有意义的标点符号称之为元字符。有人或许会问为什么要用这些标点符号呢?你回头看看那个替换问题就应该知道了,在某些时候我们只关心需要匹配的字符,但同时又需要能过滤掉很多无用的匹配信息,这就是元字符的意义。

一般讲到这里,都会开始列一串元字符什么的让你背,我觉得这并不好,毕竟看得多就没心情了,不是吗?

不过在这里还是先普及几个,下面会用到,当然,不难,应该很快就能记住。

. 匹配除换行符以外的任意字符
* 对前一个匹配的字符出现0次或多次
+ 对前一个匹配的字符出现至少一次
? 对前一个匹配的字符出现0次或一次

没错,就四个,不多吧。先记下来,然后开始下一节。

基础篇之形式

一般来说特殊的东西总要有些特殊的形式才对,不是吗?没有特殊形式的话就与活动不打甲的咸鱼有什么区别?因此我们要先介绍一下正则表达式的形式。一般的正则表达式如下:

/Regular *Expression/i

其中/表示分界符,在两个/之间的就是我们的表达式,而分界符后面的i是模式修饰符。一般一个正则表达式都由这三部分组成。下面我们来简单介绍一下。

分界符:用来区分正则表达式的开始和结束。注意,虽然我们一般都使用/作为分界符,但其实它可以是其他任意字符,只是当你在表达式里面用的时候要将分界符转义。我个人一般喜欢用!这样看上去更舒服一些。

表达式:进行主要工作的字符串,我们所谓的正则表达式也主要就是书写这部分。

模式修饰符:可选项,用来做一些额外的特殊工作,比如i就是表示该表达式不区分大小写。

OK,是不是很easy? 那你是不是能猜到上面的表达式是来匹配什么的呢?很容易吧。

Tips:空格也是字符哦。

基础篇之工具

一般来说测试正则表达式的工具有很多,比如我最初的案例都是打算使用sed和grep的,但显然用windows的人更多,没办法为了跨平台,毕竟实践出真知嘛。在这里我推荐使用chrome自带的开发者工具栏,主要有三个好处,第一js支持的元字符多,不容易碰到不识别的问题,其次可以方便你离线和在线都使用,第三就是不用额外安装其他的软件。这里假设你已经安装好了chrome浏览器,打开一个新的空白页面,接下来点击设置按钮->更多工具->JavaScript控制台。这时会打开一个新的窗口,这里便是你以后实验的地方了。使用也非常简单,你只需要记住两个命令就可以了

"你要查找的字符串".match(正则表达式)

clear()

前一个命令是你用来查找的字符串,后一个是如果屏幕满了可以清除。简单吧。具体使用方法可以像这样

"testabctext".match(/[a-c]+/)

按下回车之后你就可以看到匹配到的字符了。


基础篇之常用元字符

好了,我们现在到了最复杂的时候,没错,该来的还是要来的,现在我们需要开始记忆一些常用的元字符和表达式了。不过不用担心,很简单的,跟着我一步步来就完全没问题。

比如某天你老大有这么一个要求,小X啊,把这文章里面的数字全部改成三位一撇的形式,那么如果是你此时该怎么做?一定得先一个个找出来,然后替换吧,但数字可能不相同啊,用肉眼找显然是不合适的,用ctrl+F好像也没什么卯月。那如何是好?这时候该我们的正则出场了。

首先假设我们所要找的文本中的数字都是整数,那么可以使用以下方式

![0-9]+!

其中出现了一个中括号和一个减号,其中被中括号括起来的我们称之为字符组,它是用来匹配这个组内的任意一个字符比如![ABCabc]!就可以找到某一字符串中一个大写或小写的ABC。

Tips:如果不是全局搜索的话,那么将只匹配第一次找到的位置。

而减号我们称之为连字符。它用来表示连续的一个范围。比如上面的例子可以改写为![A-Ca-c]!很明显它的作用就是为了偷懒XD。比如我们想找英文字母,显然写52个太蛋疼了,于是便可以使用连字符。如果你还记得上一节说过的修饰符的话这个还能改写成![a-c]!i 是不是很简洁?注意,字符组中的字母顺序并没有意义,即![abc]!和![bca]!完全一样。

所以你现在也应该知道了上面的表达式是上面意思了吧。匹配从0到9的任意一个字符,且最少出现一次。

Tips:假设我们需要在字符组里面匹配连字符应该怎么处理呢?比如我想找7-5=2和6+1=7这样的四则运算时要怎么写?

显然这个已经满足我们的要求了,可以找到我们需要的数字,但似乎还不够好。试想,假设我们需要三位一撇的数字必然最少有四位,而现在不管多少位都找出来了,这似乎不好吧。

so,现在让我们来稍微改进一下。

![0-9]{4,}!

好吧,又出现了一个新的符号了,大括号。它称之为区间量词。故名思议,是用来表示一个区间的。你也许会问是什么的区间?答:它所在的前一个字符重复出现次数的区间。逗号前的为最少重复次数,逗号后的为最大重复次数。因此上面的式子表示匹配0到9中的任意一个字符,并且至少重复四次。注意你还记得我让你必须背下来的那句话吗?不记得?那请回到第一节第一句把它背熟为止。因此重复也是对应于一个字节,所以不要傻乎乎的说为什么!ing{2,}!不能匹配singing(当然如果你想匹配也是有办法的,这个在后面会说)

Tips:你现在试试把+,*和问号改写成区间量词的形式。

恩,又进了一步,不是吗?But,它仍旧有一点问题,譬如,你的文章中有一些文档编号,比如09122912345678,而这些并不是你需要找的数据,为此我们是不是应该先改进一下使得他匹配一个正整数?

!^[^0][1-9]{4,}!

这里我们出现了两个^,不过需要注意的是他们的意义是不一样的。
第一个我们称之为脱字符,它如果出现在第一个位置表示以它之后的这个字符为首开始匹配。怎么理解呢,就是说!^abc!将匹配a以a为行首且后面有bc的字符串,即假设要搜索的字符串是abctest那么就可以匹配,如果是qabctest则不能匹配。

第二个称之为排除型字符组即普通字符组以中以^开头的字符组,它表示匹配一个在不在该字符组中列出的字符。注意不是不匹配在其中的字符,而是匹配一个不在其中的字符!也就是说它还是至少要匹配一个字符!不好理解?比如如果我的字符串是I‘m a string,那么!string[^g]!,这个表达式能不能匹配到?如果按前一种说法,不匹配其中的字符,那么是不是应该可以匹配到?因为我找一个string且后面不是g的字符串,只要后面不是g,那么空当然也行,对吧。然而这是不对的,因为这句的意思是匹配一个string且后面有一个不是g的字符。一般情况两者似乎是等价的,但万一这个字符串在结尾就不一样了,前一个默认了包含不存在,而后一个默认了必须有一个字符。所以第一节的那句话请务必背熟它。

好吧,我承认这个例子其实并不好,因为这个并不能按我们预想的工作,反而连之前的都做不到,因为数字不一定在开头,其实我只是想向你们介绍一下这两个元字符<_<

Tips:如果我想在字符组中用^该这么办呢?

请稍微放下手中的刀,让我们和气生财,这就回到这上面来。前面已经说了如果我们需要排除以0开头的数字,为此我们需要使用以下表达式

![^\d]([1-9]{4,})!

我们发现这里多了两个元字符,其中\d就是[0-9],因为老外都比较懒,so……
另一个是括号,在正则表达式中括号内的内容表示一个整体,即一个新的表达式,所以我们称之为子表达式。子表达式可以作为一个整体受区间量词的影响。现在你知道上面的singing怎么写了吧。
本来这里其实不用子表达式也是可以的,去掉括号完全不会有任何的影响,之所以要用是因为用代码如果替换或者查看的时候子表达式可以单独引用,比如上面的例子中"今年的销售额是123457"我们可能匹配到"是123457",而匹配的的子表达式就是123457,而此时就可以使用反向引用\1表示123457了(反向引用将在后面讲解)

Tips:如果你只希望子表达式作为一个整体分组而无需匹配的时候,可以使用(?:)的形式


虽然这个表达式不算完美,多了一个无关字符,但应该可以完成我们所需的任务了,不是吗?不过可惜的是现实总是残酷的,我们之前假设所有的数据都是整数,这显然太理想化了,为此我们需要将其改写,以支持小数的查找。

![^\d]([1-9]{4,}(?:\.\d+)?)!

这个基本上没有什么新的东西。就是在上面的基础上加上可能有若干位小数的匹配。很好理解,不是吗?


最后我们讲一下关于分组的并列关系。假设你现在有这么一个情况,你需要在客户名单中查找张三,李四和王五同名的几个人的资料,当然你可以一个个搜,但如果你要找20个人一次次也是蛮累的。所以我们可以稍微偷懒一下,用这种方式:

!张三|李四|王五!

这么做就舒服多了,我只要写一次,剩下的就是点find,然后ctrl+C ctrl+V 就好了。尤其是在名单不按顺序排练的时候尤其好用。

Tips:分组中的并列表示或的关系,因此没有前后顺序。另外需要注意的一点是别忘了此处括号也是子表达式哦。

至此我们基础的元字符就全部介绍完了。还有一些特殊含义的字符你可以自行去查找,其实就是把我们所知道的那些封装起来罢了,比如\w就相当于[a-zA-Z0-9_\u4e00-\u9fa5],即中英文数字和底划线,毕竟老外很懒嘛XD

实例研究

其实上面说了这么多估计你也记不住<_< 所以我们不如直接来几个实例讲解一下显得明白。

实例1:

!<[^>]*>!

如果你研究过如何去除html标签的话这个估计一定见到过。现在让我们来简单说一说它的工作原理,它主要匹配第一个字符是<,然后找到除了>之外的任意字符并且将其重复0次或多次,之后再是一个>的字符串。那么当我们有一个html文档时,比如"<div>Lucky<p>Star</p>!</div>",那么当第一次匹配的时候可以找到<div>,接下来是<p>,然后</p>,最后就是</div>,那么我们只要将每次匹配的标签替换成空或者一个空白是不是就能完成去除html标签的工作了?很好理解,对吧。

不过这里面却有两个问题,不知你发现了没有?第一就是*号,它表示可以出现任意次数,那是不是不出现也是对的?既然如此,那么如果我们写一篇文档时有一个不等号是不是就错了?例如3<>5,处理之后就变成了35,这并不是我们想要的,不过不用担心,我们只需要把*改成+号,就能很简单的解决。

但随之而来的第二个问题似乎就不好办了,比如你试试在本文中用这个正则查找试试,是不是发现了什么?没错,它匹配了很多我们其实并不需要的字符串,比如"<,然后找到除了>",虽然它的确符合我们的规则,但并不是我们想要找的内容。

我之所以讲这个是因为希望你知道,正则表达式并不是一个通用的方法,它是针对某些特定字符串或文档来书写的。它不能帮助你完成所有事,因此需要理解它的思想本质,而不只是形式。就比如说在联系人中搜索手机号,如果你确信你文档中的手机号都是正确的,同时没有其他数字干扰项,那么你完全可以写成!1\d+{10}!,根本不用担心会找到错误的匹配,只有这样你才算是真正入门了。

实例2:

!(?:(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)([0-9Xx]))|(?:(\d{6})(\d{2})(\d{2})(\d{2})(\d{3}))!

这个这么长一看就知道是用来匹配身份证的。OK,我们一步步来,首先先将正则表达式进行拆分,可知总体格式是!(?:A)|(?:B)!。其中A和B非捕获匹配。接着我们先来分析A,它相当于!abcdef!,共有6个子表达式。每个单独看是不是简单许多了?6位的地区号,8位出生年月日,2位户籍号,1位性别,以及1位校验码。之所以用子表达式也是出于能获取信息的角度,因此即使不用对效果影响也不大。而B是用来匹配第一代身份证的。具体的就不用我多说了吧。

从表达式的难度来说这个比第一个还简单,但之所以列出来分析是为了让你了解面对一个复杂的正则时该如何处理。没错,就是通过换元法一层层迭代,直到找到最基本的单元,然后再一步步还原就行了。是不是觉得和做高数很相似啊:P

实例3:

!\d+\.\d+\.\d+\.\d+!


这个就很简单了,是用来匹配IP地址的。但需要注意的是其中我们在.之前加了一个\,这是为了使得.作为一个普通的字符,而不是元字符。类似的还有\\,\+等等,这里就不一一列举了。

Tips:有一个字符它虽然不是元字符,但也需要转义,请问是什么呢?

牛刀小试

好了,讲了这么多我想还是实际操作一下记忆来的快。为此我特意选择了几个小问题,你可以尝试解决一下,应该不会太难。

1、请写一个验证邮箱的正则表达式(很常用不是吗?)

2、试着完善一下我刚才提过的匹配手机号的正则(又一个常用的)

3、请写出本文提到的第一个问题的正则————将所有文章名称中的正则表达式替换为Regular Expression

4、请写出匹配空行的正则表达式。(注意这里要包含有空格和无空格两种情况)

5、在客户名单中搜索国内的固定电话号码(注意区号可能有括号,也可能没有)

6、当一个数字过大时在EXCEL中或许会变成科学计数法,那么请尝试写一个匹配科学记数法的正则。

*7、尝试写一个正则表达式检验一个数是否为素数。(提示:可以手动将数字转成其他格式)

除了最后一题作为思考题有些略难并且会用到下一节的知识之外,其他应该看完这篇文章之后仔细想一下都能写出来。

结语
至此我们基础篇的课程到此全部结束。基本上只要你多加练习,融会贯通应该足够应付生活中常见的问题了。如果你有兴趣进一步学习的话,可以期待后续的中级篇课程,比如之前说的环视这类都会在其中讲解(如果我没有弃坑的话╮(╯-╰)╭)

 

最后的最后,如果觉得写的还可以请欢迎转载,不过记得注明出处哦(●'◡'●)

转载于:https://www.cnblogs.com/buckets/p/5652985.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值