javascript学习笔记(8)--正则表达式

字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在。比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以复用
正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的

在正则表达式中,如果直接给出字符,就是精确匹配
\d可以匹配一个数字
\w可以匹配一个字母或数字
'00\d'可以匹配’007’,但无法匹配'00A',其实就是三位以00开头的数字表达式
'\d\d\d’可以匹配任意三位数字
'\w\w’可以匹配任意两位数字或者字母
&!排除,比如说{\w&!\d}匹配大小写字母

.可以匹配任意字符
'js.'可以匹配’jsp’、‘jss’、'js!'等等,其实就是三位以js开头的任意表达式
上面讲的这些都是匹配定长的表达式

当然也可以匹配变长表达式
*表示任意个字符(包括0个)
+表示至少一个字符
?表示0个或1个字符
{n}表示n个字符
{n,m}表示n-m个字符

\s可以匹配一个空白字符[ \t\n\x0B\f\r]
解释一下含义,按照顺序依次是水平制表(HT) (跳到下一个TAB位置),换行,垂直tab,换页(FF),将当前位置移到下页开头,回车(CR) ,将当前位置移到本行开头

如果要匹配’010-12345’这样的号码
由于’-'是特殊字符,在正则表达式中,要用'\'转义,所以,上面的正则是\d{3}\-\d{3,8},\d{3}代表3位数字,-代表-,\d{3,8}`代表3-8位数字

但是,这种匹配没有滤过空格效果,仍然无法匹配’010 - 12345’,所以需要更复杂的匹配

[0-9a-zA-Z_]可以匹配一个数字、字母或者下划线;
[0-9a-zA-Z_]+可以匹配至少由一个数字、字母或者下划线组成的字符串
[a-zA-Z_$][0-9a-zA-Z_$]*可以匹配由字母或下划线、$开头,后接任意个由数字、字母或者下划线、$组成的字符串,也就是JavaScript允许的变量名
[a-zA-Z_$][0-9a-zA-Z_$]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)

通过上面的例子我想你也发现了,*,+,?,{n},{n,M}不是单独出现,必须紧跟表达式后面,并且对前面的表达式作用

^表示行的开头,^\d表示必须以数字开头。
$表示行的结束,\d$表示必须以数字结束。

^js$这个表达式的含义就是以j开头,以s结尾,并且长度只能是2,就变成了整行匹配,就只能匹配’js’了
如果要匹配j…s的话呢,我们可以^j.*s$

在JavaScript中使用正则表达式了
JavaScript有两种方式创建一个正则表达式
第一种方式是直接通过/正则表达式/写出来,第二种方式是通过new RegExp(‘正则表达式’)创建一个RegExp对象
两种写法是一样的

var re1 = /ABC\-001/; 
var re2 = new RegExp('ABC\\-001'); 
re1; // /ABC\-001/ 
re2; // /ABC\-001/

注意,如果使用第二种写法,因为字符串的转义问题,字符串的两个\\实际上是一个\

判断正则表达式是否匹配,使用test()

var re = /^\d{3}\-\d{3,8}$/;//以3个数字开头加-再以3-8位数字结尾
 re.test('010-12345'); // true
re.test('010-1234x'); // false
re.test('010 12345'); // false

同时使用正则表达式也方便了我们切分字符串
比如说我们要获得有效的输入,把空格切除,因为split作用的原理
看案例

'a b c'.split(' '); //Array(3) [ "a", "b", "c" ]
'a  b   c'.split(' ');//Array(6) [ "a", "", "b", "", "", "c" ]

第一个由于a,b,c互相只间隔一个空格,所以没问题,但第二个中间间隔了若干个空格,所以…会切出空格来
那么我们来用用正则表达式
\s对应的是空格正则表达式一定要装在//里,而且也不需要’'了,直接用//就可以

'a  b   c'.split(/\s+/);//效果不错Array(3) [ "a", "b", "c" ]

我们可以在丰富一下,把,也切掉

'a  ,b  , c'.split(/[\s\,]+/);//Array(3) [ "a", "b", "c" ]

再丰富一下,把;.也切除

'a..  ,b ;; , c'.split(/[\s\,\.\;]+/);//Array(3) [ "a", "b", "c" ]

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)
^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码,第一个组就是前3位,第二个组是-后面的3-8位

var re = /^(\d{3})-(\d{3,8})$/; 
re.exec('010-12345');//Array(3) [ "010-12345", "010", "12345" ]
re.exec('010 12345'); // null 

如果正则表达式中定义了组,就可以在RegExp对象上用exec()方法提取出子串来
exec()方法在匹配成功后,会返回一个Array,第一个元素是正则表达式匹配到的整个字符串,后面的字符串表示匹配成功的子串
exec()方法在匹配失败时返回null

var re = /(0[0-9]|1[0-9]|2[0-3])\:([0-5][0-9])\:([0-5][0-9])/; 
re.exec('19:05:30'); // Array(4) [ "19:05:30", "19", "05", "30" ]

这个re有点复杂,我们来分析一下,首先是0[0-9]|1[0-9]|2[0-3],代表则24个小时的时间,然后一个:,再来一个分钟的格式,再来一个:,再来一个秒

最开始没搞清楚分组的原理,后来才发现有(),()内算是一个组,就会把匹配的分割出来

正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符

var re = /^(\d+)(0*)$/; 
re.exec('102300'); // ['102300', '102300', ''] 

这个1的匹配效果是,第一组是不止一个数字,第二组是若干个零
由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了

所以有时候必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配

var re = /^(\d+?)(0*)$/; 
re.exec('102300'); // ['102300', '1023', '00']

JavaScript的正则表达式还有几个特殊的标志,最常用的是g,表示全局匹配

var r1 = /test/g; // 等价于
var r2 = new RegExp('test', 'g'); 

全局匹配可以多次执行exec()方法来搜索一个匹配的字符串,当我们指定g标志后,每次运行exec(),正则表达式本身会更新lastIndex属性,表示上次匹配到的最后索引
不知道咋回事
测试的时候怪怪的,和书上不太一样
下面是我测试的结果,感觉这个匹配有点随性

var s = 'JavaScript, VBScript, JScript and ECMAScript';
var re=/[a-zA-Z]+Script/g;
re.exec(s);//Array [ "VBScript" ]
re.lastIndex;//29
re.exec(s);//null
re.lastIndex;//10
re.exec(s);//Array [ "JScript" ]
re.lastIndex;//44
re.exec(s);//Array [ "JavaScript" ]
re.lastIndex;//20
re.exec(s);//Array [ "ECMAScript" ]
re.lastIndex;//0
re.exec(s);//Array [ "VBScript" ]
re.lastIndex;//29
re.exec(s);//null

如果不使用全局匹配,会是这个效果

var s = 'JavaScript, VBScript, JScript and ECMAScript';
var re=/[a-zA-Z]+Script/;
re.exec(s);//Array [ "JavaScript" ]
re.exec(s);//Array [ "JavaScript" ]

全局匹配类似搜索,因此不能使用/^…$/,那样只会最多匹配一次
正则表达式还可以指定i标志,表示忽略大小写,
m标志,表示执行多行匹配

猜你喜欢

转载自blog.csdn.net/azraelxuemo/article/details/106864162