前端的正则表达式

正则的用法

  const regex = /abc/g
  const regex2 = new RegExp('abc', 'g')
  regex 和 regex2的匹配内容是一样的,都是全局匹配abc字符串,
  只不过regex是用正则表达式字面量,而regex2是调用RegExp构造函数写法。

元字符

^   匹配字符串的开始               /^a/ 表示匹配以a开始
$   匹配字符串的结尾               /a$/ 表示匹配以a结尾
\d  [0-9] 表示0-9的任意一位数字    /^\d/ 表示以一个数字开始
\D  [^0-9] 表示除数字外的任意字符   /^\D/ 表示不能以数字开始
\w  [0-9a-zA-Z_] 表示数字、大小写字母和下划线
\W  [^0-9a-zA-Z_] 表示非单词字符
\s  [\t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符
\S  [^ \t\v\n\r\f] 非空白符
\b  匹配一个单词边界
\B  匹配非单词边界
.   [^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。
    要是想匹配字符串.需要转译\.

量词

{m, n} 出现了m-n次
{m}    出现了m次
{m,}   出现了至少mci
{,n}   最多出现了n次
?     出现了0次或1次, 等价于{0,1}
+      最少出现了1次,等价于{1,}
*      出现0次或多次,等价于{0,}

贪婪与惰性

贪婪就是尽可能多的匹配,而惰性就是尽可能少的匹配

贪婪匹配写法:量词后面加个*
惰性匹配写法:量词后面加个?

/.*bbb/g.exec('abbbaabbbaaabbb1234')  // abbbaabbbaaabbb
/.*?bbb/g.exec('abbbaabbbaaabbb1234') // abbb

贪婪模式用于匹配优先量词修饰的子表达式
惰性模式用于匹配忽略优先量词修饰子表达式

分组

正则中一对()即为一个分组

/(abx).*/.test('abxsss') 其中(abx) 就是一个分组

可使用构造函数的全局属性1至9来获取

/(abx).*/.exec('abxsss')
console.log(RegExp.$1)  // abx
console.log(RegExp.$_)  // abxsss

只能表示到1-9, 如果分组超过了九组,那就没法表示了

反向引用

/\d{4}(-|\/|\.)\d{2}\1\d{2}/
注意里面的\1,表示的引用之前的那个分组(-|\/|\.)。
不管它匹配到什么(比如-),\1都匹配那个同样的具体某个字符。
我们知道了\1的含义后,那么\2和\3的概念也就理解了,即分别指代第二个和第三个分组。

这里的分组只是在此之前的分组,在该反向引用之后的无法表示。
/\d{4}(-|\/|\.)\d{2}\2(\d{2})/
这里不能用\2来引用(\d{2})分组,这里只是当作匹配\2去匹配,而不是当作分组的引用

具名分组

/(\d{4}(-|\/|\.))\d{2}\2\d{2}/
这里的第二个分组应该是(-|\/|\.)

那/^((\d)(\d(\d)))$/的分组又是怎么分的呢?
对于过多括号嵌套情况,分清分组比较容易出错
括号嵌套的分组,应该从第一个(往里面数,每遇到一个(就是一个新的分组

具名分组就很好的解决了这个问题
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
这里就是用到了具名分组
可以通过\k<month>引用具体是哪个分组
/(?<year>\d{4})-(?<month>\d{2})-\k<month>/
具名分组结果会放在group属性里面

非捕获分组

也可以通过非捕获分组来降低分组的复杂度。非捕获分组是指不会将(?:)中的正则化为一个分组。
/^(?:(?:\d)(\d(\d)))\2$/
这里\2引用的分组是(\d)

零宽断言(环视)和负零宽断言

/z(?=x)y/ 表示z后面紧跟着的是x,但是不匹配x
/z(?!x)y/ 表示z后面紧跟着的不是x,但是不匹配x

/z(?<=x)y/ 表示y前面紧跟着的是x,但是不匹配x
/z(?<!x)y/ 表示y前面紧跟着的不是x,但是不匹配x


修饰符

1. g → global 全局搜索
2. i → ignoreCase 忽略大小写,大小写不敏感
3. m → multiline 换行
4. y → sticky  “粘连”修饰符  后一次匹配都从上一次匹配成功的下一个位置开始
5. u → unicode Unicode 字符表示法

lastIndex

是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。
只有正则表达式使用了表示全局检索的 "g" 标志时,该属性才会起作用。此时应用下面的规则:

如果 lastIndex 大于字符串的长度,则 regexp.test 和 regexp.exec 将会匹配失败,然后 lastIndex 被设置为 0。
如果 lastIndex 等于字符串的长度,且该正则表达式匹配空字符串,则该正则表达式匹配从 lastIndex 开始的字符串。(then the regular expression matches input starting at lastIndex.)
如果 lastIndex 等于字符串的长度,且该正则表达式不匹配空字符串 ,则该正则表达式不匹配字符串,lastIndex 被设置为 0.。
否则,lastIndex 被设置为紧随最近一次成功匹配的下一个位置。

练习


1.检测是否满足
  2016-06-12
  2016/06/12
  2016.06.12
  这三种格式


/\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/.test('2012/12/13')
这样写有一个问题,那就是
/\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/.test('2012/12-13') 也是true

所以这里就得用到分组引用了
/\d{4}(-|\/|\.)\d{2}\1\d{2}/.test('2012/12-13') false
/\d{4}(-|\/|\.)\d{2}\1\d{2}/.test('2012/12/13') true
这里的\1指的是第一个分组匹配到了什么,这里就是什么


2.将 2016/12/04 改为 12/04/2016
传统做法可能是需要先以'/'分割字符串,然后在就行字符串拼接
现在可以这样写

'2016/12/04'.replace(/(\d{4})\/(\d{2})\/(\d{2})/, '$2/$3/$1')

或者

'2016/12/04'.replace(/(\d{4})\/(\d{2})\/(\d{2})/, function(...arg) {
  return arg[2]+'/' + arg[3]+'/' + arg[1]
})

let result = /(?<year>\d{4})\/(?<month>\d{2})\/(?<day>\d{2})/.exec('2016/12/04')
console.log(result)
// 0: "2016/12/04"
// 1: "2016"
// 2: "12"
// 3: "04"
// groups:
//  day: "04"
//  month: "12"
//  year: "2016"
// index: 0
// input: "2016/12/04" 


3.可以正确筛选以下规则
'x=5'           5
'abcx=5'        5
'abc x=5'       5
'x=abc'         null

str.match(/(?<=x=)\d+/)

4.可以正确筛选以下规则
'x=5'           5
'abcx=5'        null
'abc x=5'       5
'x=abc'         null
这个和上面的区别就在于x=之前是不是一个边界
str.match(/(?<=\bx=)\d+/)
不可以用一下的正则匹配
str.match(/(?<=\s+x=)\d+/)
因为这样的话'x=5'就不匹配了

5.可以正确筛选以下规则
'one "two three" four five six "seven eight" nine' 
to 
'"two three" four five six "seven eight"'

str.match(/".*"/g)

6.可以正确筛选以下规则
'one "two three" four five six "seven eight" nine' 
to 
['"two three"', '"seven eight"']

str.match(/".*?"/g)
这里和上面的区别就在于一个是贪婪匹配而这个是惰性匹配

7.可以正确筛选以下规则
'@@whatever@@@@whatever2@@' 
to
 '<blink>whatever</blink><blink>whatever2</blink>'

str.replace(/(@@)(.*?)(@@)/g, function(...arg) {
  return '<blink>'+ arg[2]+'</blink>'
})
这里不能简单的将'@@'替换成'<blink>',因为'@@'必须是成对出现的

8.可以正确筛选以下规则
var text = "First line\nsecond line";
var regex = /(\S+) line\n?/y;

var match = regex.exec(text);
match[1];  // "First"

var match2 = regex.exec(text);
match2[1]// "Second"

var match3 = regex.exec(text);
match3 === null //"true"
这里是开启粘滞匹配的一个事例

9.测试以下代码
var re=/^\w$/g
re.test('a')  true
re.test('a')  false
re.test('a')  true
re.test('a')  false
re.test('a')  true
re.test('a')  false
re.test('a')  true
re.test('a')  false
...

为什么会出现这种情况呢?
这里就涉及到了lastIndex了,
var re=/^\w$/g
re.lastIndex = 0

re.test('a')  true
这里正则匹配完成后re.lastIndex = 1

re.test('a')  false
因为re.lastIndex = 1了,所以这里的re.test就会失效,当然re.exec也会失效。
可以理解为这时正则是从1这个位置去匹配的,所以匹配不到任何东西.只会就会再次把lastIndex改为0
re.lastIndex = 0

re.test('a')  true
又重新从头开始匹配了,所以这里又再次为true了

结尾

本文并没有举例传统的一下正则,如邮箱格式,手机号码格式等,因为这些正则可以搜到很多。本文主要是用到了一下稍微高阶一点的正则方法。古人云:用好了正则,可以帮我们省下5000行代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容