关于正则表达式我所知道的

797 阅读3分钟

关键词: 正则 校验 字符串

本文所作的主要目的是为了要用到正则的时候,能尽快解决问题,对正则有个大致概念。

所以开篇内容类似《飞行手册》以案例和工具为主。

好用的正则工具

常用的正则

//Email地址
/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/g

//手机号码
/^1[3-9]\d{9}$/g

//固定电话
/^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$/

//身份证号码(15位、18位数字)
/^\d{15}|\d{18}$/

//中国邮政编码(中国邮政编码为6位数字)
/[1-9]\d{5}(?!\d)/

//URL
/^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/

//匹配HTML标签    
/<(\w+)[^>]*>(.*?<\/\1>)?/

//html注释
/<!--[\s\S]*?-->/g

// 取闭合标签 match
/(<span class="blue">)([\S\s]*?)(\<\/span>)/g

// 替换标签
replace(/\<i\>(.*?)\<\/i\>/g,"<span>$1</span>")

//img src
/<img [^>]*src=['"]([^'"]+)[^>]*>/

//金额校验,精确到2位小数
/^[0-9]+(.[0-9]{2})?$/

// 1000->1,000 金额格式化
replace(/\B(?=(\d{3})+(?!\d))/g,',')

//输入数字或者小数
/^\d*\.?\d*$/g

概念定义

正则表达式本质是文本处理工具,是由普通字符和特殊字符组成的文字模板。

正则规则

匹配字符

字符说明
.匹配除换行符以外的任意字符
\转义符
\d匹配数字, 等价于字符组[0-9]
\D匹配非数字的任意字符, 等价于[^0-9]
\w匹配字母, 数字, 下划线
\W匹配除字母,数字,下划线之外的任意字符
\s匹配任意的空白符(包括制表符,空格,换行等)
\S匹配非空白的任意字符
[...]字符组
[^...]排除性字符组
{min,max}量词,至少出现 min 次,最多出现 max 次
{min}量词,出现 min 次
?量词,出现0次或者1次,等价于{0,1}
+量词,至少出现1次,等价于{1,}
*量词,出现一次,等价于{0,}
'abc abbc abbbc abbbbc abbbbbc abbbbbbc'.match(/ab{2,5}c/g)
//  ['abbc', 'abbbc', 'abbbbc', 'abbbbbc']
'a0b a1b a2b a3b a4b'.match(/a[0-3]b/g)
//  ['a0b', 'a1b', 'a2b', 'a3b']

贪婪 & 非贪婪

正则本身是贪婪的,默认会尽可能的多匹配符合模式的字符。

可以量词后面加一个?,就变成非贪婪。让正则表达式尽可能少的匹配,一旦成功就不再继续尝试。

贪婪量词非贪婪量词
{m,n}{m,n}?
{m,}{m,}?
???
++?
**?
const str = '123 1234 12345 123456'
// 贪婪
str.match(/\d{2,5}/g)
// 非贪婪
str.match(/\d{2,5}?/g)

// ['123', '1234', '12345', '12345']
// ['12', '12', '34', '12', '34', '12', '34', '56']

const nstr = "123456789"
nstr.replace(/\d{3,6}/,'换成这个')
nstr.replace(/\d{3,6}?/,'换成这个')
// 替换成这个789
// 替换成这个456789

多选分支

按照分支的顺序逐个匹配,当前面的分支满足要求了,则舍弃后面的分支。

ab|cd => 匹配'ab'或者'cd'字符子串。

'good idea, nice try.'.match(/good|nice/g)
// ['good', 'nice']

应用题

  • 匹配id
  • 匹配16进制的颜色值
  • 匹配24小时制时间
  • 匹配日期

答案解析

匹配位置

字符说明
\b单词的边界,匹配单词开始或结束的位置
\B非单词边界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B 的。
^abc$字符串开始、结束的位置
(?=p)正向先行断言。符合p(子模式)前面的那个位置。
(?!p)负向先行断言。(?=p)取反的意思
(?<=p)符合p(子模式)后面的那个位置。
(?<!p)(?<=p)取反
// 单词边界
'[[show_me_code.html]]'.replace(/\b/g, '| ') 
// 非单词边界
'[[show_me_code.html]]'.replace(/\B/g, '| ') 

// '[[| show_me_code| .| html| ]]'
// '| [| [s| h| o| w| _| m| e| _| c| o| d| e.h| t| m| l]| ]| '

// 正向先行断言 在符合条件的地方前面
'show_me_code'.replace(/(?=show)/g, '| ')
// 负向先行断言 在符合条件的地方以外
'show_me_code'.replace(/(?!show)/g, '| ')

// '| show_me_code'
// s| h| o| w| _| m| e| _| c| o| d| e| 

// 符合p(子模式)后面的位置
'show_me_code'.replace(/(?<=show)/g, '| ')
// (?<=p)匹配到的位置之外的位置
'show_me_code'.replace(/(?<!show)/g, '| ')

// 'show| _me_code'
// '| s| h| o| w_| m| e| _| c| o| d| e| '

应用题

  • 数字的千分位分割法
  • 手机号3-4-4分割
  • 验证密码的合法性

答案解析

分组

让量词作用于一个整体。分组中的分支结构则有点像 ||

// ab 是一个分组
'ababa abbb ababab'.match(/(ab)+/g)
console.log(string) 
// ["abab", "ab", "ababab"]

/I love (JavaScript|Regular Expression)/.test('I love JavaScript') // true
/I love (JavaScript|Regular Expression)/.test('I love Regular Expression') // true

分组引用

常用来界定重复限定符的范围, 以及将字符分组. 如: (ab)+ 可以匹配abab..等, 其中 ab 便是一个分组。

// 提取年月日
const reg = /(\d{4})-(\d{2})-(\d{2})/
const str = '2023-03-06'

reg.test(str)

// 反向引用
console.log(RegExp.$1) // 2023
console.log(RegExp.$2) // 03
console.log(RegExp.$3) // 06

// 数据替换
// 2023-03-06 -> 03/06/2023
'2023-03-06'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1')

'2023-03-06'.replace(/(\d{4})-(\d{2})-(\d{2})/, () => {
    return RegExp.$2 + '/' + RegExp.$3 + '/' + RegExp.$1
})

'2023-03-06'.replace(/(\d{4})-(\d{2})-(\d{2})/, ($0, $1, $2, $3) => {
    return $2 + '/' + $3 + '/' + $1
})

非捕获性括号

非捕获性分组, 匹配正则的位置, 但不捕获匹配结果。

也就是说不创建反向引用, 就好像没有括号一样.

"#808080".replace(/#(?:\d+)/,"$1"+"~~")
// $1~~
console.log(RegExp.$1); // ""

"#808080".replace(/#(\d+)/,"$1"+"~~")
// 808080~~
console.log(RegExp.$1); // "808080"

应用题

  • trim 方法模拟
  • 将每个单词的首字母大写 my name is => My Name Is
  • 驼峰化 -moz-transform => MozTransform
  • 中划线化 MozTransform => -moz-transform
  • HTML转义和反转义
  • 匹配成对的标签

答案解析

参考资料