从零开始学正则(二)

 壹 ? 引

我在从零开始学正则(一)这篇文章中介绍了正则横向模糊与纵向模糊匹配模式,以及常用的字符组与量词,掌握了这些其实已经算正则入门了。在文尾留下了两个正则问题,请写出匹配24小时制时间与16进制颜色的正则,在学习第二章之前我们先搞定这两个问题。

24小时制时间格式一般是09:30这样,小时的第一位数字可能是[0-2]三种情况之一,当为0,1时,第二位数字可以是[0-9]任意数字,当为2时第二位数字只能是0-3之间的数字。第三位数字只能是0-5之间的数字,最后一位数字只能是0-9之间。

我们只用对于小时的两种情况做个分支,所以正则可以写成这样:

var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;
regex.test("00:07"); //true
regex.test("23:59"); //true

注意,匹配小时的分支我们使用了一对圆括号包裹,表示这是一个组,而组内包含了两个分支情况,如果不加圆括号正则解析时会将管道符 | 左右两侧理解成两个分支,如下图,很明显这不是我们想要的规则:

其次,在正则内部开头和结尾我们分别使用了^$两个符号,这表示正则匹配时严格以字符串开头和结尾中间的内容为匹配对象,如果不加效果就是这样:

var regex = /([01][0-9]|[2][0-3]):[0-5][0-9]/;
regex.test("0000:0709");//true

这里是因为字符串中间有一部分是符合规则的,因此还是符合匹配规则,所以如果我们想匹配一个字段从头到尾是否符合规则,一定得记得加上^$符号。

我们再来分析16进制颜色,老实说我还真不知道16进制颜色范围是多少,查了下,每个字母范围均为[0-9a-fA-F],但由于颜色值可以简写,比如 #ffffff 可以简写成 #fff,所以存在6位与3位的情况,正则可以这么写:

var regex = /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/;
regex.test("#e4393c"); //true
regex.test("#2b99ff"); //true

那么上章节留下的问题就说到这,开始第二章学习。说在前面,正则学习系列文章均为我阅读 老姚《JavaScript正则迷你书》的读书笔记,文中所有正则图解均使用regulex制作。本章节主要讲述如何匹配字符位置,那么本文开始!

 贰 ? 正则中的位置

注意,这里所说的位置并不是我们遍历数组时所使用的索引概念,正则匹配的位置又称为锚,是指相邻字符之间的位置,这里借用原书中的图解,每个箭头就是一个位置:

正则使用中,匹配位置的字符又称为锚,在文章开头我们已经见过了 ^$ 两个锚,我们来验证下位置的概念,看个简单的例子:

var str = ‘听风是风‘;
var regex = /^|$/g;
var result = str.replace(regex, ‘?‘); //?听风是风?

可以看到两个位置被替换成了花朵,此时字符串的开头位置与结尾位置发生了变化,开头变成了花朵左边,结尾位置变为第二朵花的右边。

 叁 ? 理解正则的锚

出了常用的^$,在ES5中一共包含六个锚,分别是^、$、\b、\B、(?=p)、(?!p),我们一一细说。

^ 脱字符:匹配开头,在多行中匹配行开头,比如:

var str = ‘听风\n是风‘;
var regex = /^/mg;
var result = str.replace(regex, ‘?‘);

注意,正则结尾添加了一个mg,g(global)前面有解释表示全局匹配,表示一行从左到右完整匹配一遍,而m(more)表示多行匹配,mg就是多行全局匹配,每行不管文本多长,都完全匹配一遍。

$美元符号:匹配结尾,在多行中匹配行尾。

\b单词边界:表示\w(单词字符)与\W(非单词字符)之间,\w(单词字符)与 ^ (脱字符)之间,以及\w (单词字符)与$ 之间的位置,有点难理解,先看个例子:

var str = ‘[echo].123‘;
var regex = /\b/g;
var result = str.replace(regex, ‘?‘); //[?echo?].?123?

上面解析有点长,我们缩短点,\b表示\w与\W、^、$之间的位置,而\w范围是[0-9a-zA-Z_],那么我们再看上面的例子,为了方便理解,我们拆分细说:

从左往右看,首先 ^ 与 [ 之间不满足,再到 [ 与 e 之间,[ 是非单词符而 e 是单词符,满足条件。

echo由于四个字母都是单词符,直接跳过,o 与 ] 又满足了条件。

] 与 . 之间很明显不符合,再看 . 与1又满足了条件。

123都是单词符,跳过,直接到了尾部 3 与 $,满足条件。

\B非单词边界,意思与\b相反,匹配\w 与 \w、 \W 与 \W、^ 与 \W,\W 与 $ 之间的位置,还是上面的例子,我们改改匹配条件:

var str = ‘[echo].123‘;
var regex = /\B/g;
var result = str.replace(regex, ‘?‘); //?[e?c?h?o]?.1?2?3

可以看到 ^ 与 [ 之间,以及单词符与单词符之间都满足了条件。

(?=p)正向先行断言:p表示一个匹配模式,即匹配所有满足条件p的字段的前面位置,有点绕口,看个简单的例子:

var str = ‘hello‘;
var regex = /(?=l)/g;
var result = "hello".replace(regex, ‘?‘); //he?l?lo

这里就是先在字符串中找到字母 l,然后再找到 l 前面的位置就是目标位置。为了方便,直接利用前面位置理解的图,也就是这两个红框了:

那么(?!p)与(?=p)就是反过来的,还是上面的例子,我们改改条件,也就是下图中绿框中的位置:

var str = ‘hello‘;
var regex = /(?!l)/g;
var result = "hello".replace(regex, ‘?‘); //?h?ell?o?

如果不看这个图,我不知道大家有没有这样的疑惑,不对啊,前面解释 \b单词边界时,是从 ^脱字符 开始判断的,脱字符也不满足条件前面也应该加朵?,最终输出难道不应该是 ??h?ell?o? 这样吗?o后面有? 是因为o后面还有个 $,$不满足条件所以才这样啊。

记住,^和$是两个位置,^$是两个位置,^$是两个位置,不要将^$主动理解成两个隐藏字符,我们现在是在匹配位置。

所以 /(?=l)/g 就是在找 l 前面的位置,而 /(?!l)/g 本质上来说就是找不是字母 l 前面的其它所有位置!

那为什么 \b单词边界还能从 ^ 开始判断呢,因为概念就包含了判断\w 与 ^ 之间的位置,在判断单词边界时,^$这两个特殊位置就像两个隐藏字符一样,也成了判断位置的条件(用位置判断位置,骚的不行)。而在判断(?!p)与(?=p)时,主要p不是^$,那么此时的 ^$ 单纯作为两个位置,不会主动作为判断条件参与判断,这一点千万不要弄混了!!!

 肆 ? 位置的特性

读到这里我也有点迷糊,本来就是找位置,你 ^$ 作为位置应该是被找的对象,怎么还反客为主成了找位置的条件了,位置和位置之间难道还有位置?正则里还真是这样!

我们可以将位置理解成一个空字符" ",就像上图的箭头,一个hello可以写成这样:

"hello" = "" + "h" + "" + "e" + "" + "l" + "" + "l" + "" + "o" + "";

它甚至还能写成这样,站在位置的角度,位置能是无限个:

"hello" = "" + "" + "hello"

以正则的角度,我们测试一个单词是否为hello甚至可以写成这样:

var str = ‘hello‘;
var regex = /^^^^^hello$$$$$$$$$$$$/g;
var result = regex.test(str); //true

当然这是我们站在匹配正则位置的角度抽象理解成这样的,毕竟真的给字符串加空格,字符串就真的变样了。

我也不太理解这个位置概念为啥要设计成这样....脑壳疼,反正大家理解这个点就好了。

\b单词边界会拿^$这两个特殊位置作为判断其它位置的条件,记住这一点。

 伍 ? 总

那么到这里,第二章就说完了,内容其实真不多,只是这个位置让人有点懵,多回顾一下就好了。这里我整理了一份简单的思维导图,大家可以看着回顾一下本篇知识点:

最后留几个思考题,请写出一个正则将"12345678"变成千位分隔符表示法 "12,345,678",以及写一个正则验证密码,密码格式必须在6-12位之间,且至少包含数字,小写字符,大写字符其中两种。

那么本文就到这里了,我得学习第三章了!

原文地址:https://www.cnblogs.com/echolun/p/12040467.html

时间: 2024-09-30 05:54:03

从零开始学正则(二)的相关文章

从零开始学正则(四)

壹 ? 引 我在从零开始学正则(三)这篇博客中介绍了分组引用与反向引用的概念,灵活利用分组能让我们的正则表达式更为简洁.在文章结尾我们留下了两个问题,一问使用正则模拟实现 trim方法:二问将my name is echo每个单词首字母转为大写. 我们先来分析第一个问题,trim属于String方法,能去除字符串头尾空格,所以我们只需要写一个正则匹配头尾空格将其替换成空即可.空格属于空白符,所以这里需要使用字符组  \s ,空格可能有多个,所以要使用量词 + :其次我们需要匹配紧接开头后与结尾前

从零开始学正则(一)

 壹 ? 我为什么学正则 正则表达式是从侧面衡量一个程序员水平的标准,可以很肯定的说没有哪位高级开发不懂正则.在前端开发中使用正则表达式最频繁的场景莫过于表单验证,判断邮箱,判断手机号格式等等,那么我是怎么解决这些问题的呢,打开百度,输入“正则验证手机”回车,复制粘贴即可.我想大家应该看过不少关于常用正则整理的文章,我不理解正则,反正从来也记下来了. 古人云,熟读唐诗三百首,不会做诗也会吟.我会花三周左右系统化学习正则表达式,若你有兴趣可以与我一同学习(一起受苦),我相信学成之后即使无法立刻写出

从零开始学正则(七:终章)

 壹 ? 引 花了差不多半个月的晚上时间,正则入门学习也步入尾声了,当然正则的学习还将继续.不得不说学习成效非常明显,已能看懂大部分正则以及写出不太复杂的正则,比如帮组长写正则验证文件路径正确性,再如进产品页根据页面地址获取产品id: let pathname = '/webtoprint/dynamicsize-gamebox-2033986.html'; let productId = pathname.match(/\-(\d+)\./)[1]; //2033986 虽然正则都不难,但是相

从零开始学JavaScript二(基本概念)

基本概念 一.区分大小写 在ECMAScript中的一切(变量.函数名.操作符)都是区分大小写的. 如变量名test和Test分别表示两个不同的变量, 二.标识符 所谓标识符,就是指变量.函数.属性的名字,或者函数的参数.标识符按照下列格式规则组合起来的一或者多个字符: 第一个字符必须为一个字母.下划线(_).或者一个美元符号($); 其它字符可以是字母.下划线.美元符号或数字. ECMAScript标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写,例如:firstSe

Java从零开始学十二(构造方法)

一.构造方法 构造方法的主要目的是为类中的属性初始化 二.构造方法的定义格式 class 类名称{ 访问权限 类名称(类型1 参数1,类型2 参数2,…){ 程序语句 ; …    // 构造方法没有返回值 } } 在构造方法的声明中读者一定要牢记以下几点: · 构造方法的名称必须与类名称一致 · 构造方法的声明处不能有任何返回值类型的声明 · 不能在构造方法中使用return返回一个值 class Person { private String name; // 声明姓名属性 private

从零开始学MySQL(二)

鉴于上节篇幅以安装为主,因此对于调用mysql所需要使用的“命令”只是略微提及.随之而来就会带给读者诸多不解了,因为你会思考,这串长长的字符到底有什么特殊的含义呢?聪明的你可能早就抱着好奇心去“摆渡”一下了.而答案不过是在连接mysql服务器时,应当使用的一种方式——即,身份验证而已.另外一个情境是,先前笔者每每登录总是需要键入这么多个字符,久而久之就难免抱怨,“哎呀,好烦,每次登录都要设定这些连接参数,诸如‘mysql -p -uroot -h......(此处省略更多参数)’,既然无法跳过这

从零开始学Python3(二)--list和tuple

list和tuple都是python内置的数据类型 list list是一个可变的有序表,所以,可以往list中追加元素到末尾: >>> a = ['a','b','c',1,2,3] >>> a.append(4) >>> a ['a', 'b', 'c', 1, 2, 3, 4] 也可以把元素插入到指定的位置,比如索引号为1的位置: (insert比append的性能差很多) >>> a.insert(1,'d') >>

从零开始学Shell(二)

$1,$2....${10},${11},[root@localhost cee]# cat p.sh #!/bin/bash#file_name:p.shecho $1 $2[root@localhost cee]# sh p.sh 123 william123 william[root@localhost cee]# sh p.sh 123 william 456123 william[root@localhost cee]# [root@localhost cee]# echo \${1.

从零开始学安全(二十九)●上传漏洞代码分析

这是一段php 对文件上传黑名单限制  出现看没什么问题  但对包含函数运用错误以及对php 各种类型解析也不了解 1第一个问题   拿上传的后缀名 去和黑名单 对比   这种代码首先逻辑上就要明确 不是拿后缀名和黑名单数组对比是否包含 因为后缀可以是 php3  而是 对黑名单里的每个字符串逐个 去对比 上传后缀     应该用 输入字符和数组里的字符进行对比是否有包含关系 第二个问题 黑名单验证不全 |asp/aspx|asp,aspx,asa,asax,ascx,ashx,asmx,cer