JS正则表达式学习笔记
什么是正则表达式
正则表达式是用于匹配字符串中字符组合的模式。
正则表达式如何创建
- 字面量:
/pattern/flags
=>/^\d+$/g
- 构造函数:
new RegExp(pattern, [, flags])
=>new RegExp('^\d+$', 'g')
- 工厂方法:
RegExp(pattern, [, flags])
=>RegExp('^\d+$', 'g')
字面量和构造函数的区别
- 正则字面量,
/ab+c/g
提供正则的编译状态,当成常量使用,在循环中使用正则字面量,不会进行重复编译 - 构造函数,
new RegExp('ab+c', 'g')
提供正则的运行时编译状态,一般用在不清楚使用的模式或模式是从另一个源获得如用户输入,这种情况可以使用构造函数 - 在预先知道
pattern
的情况下用正则字面量,字面量正则表达式当成常量使用,需要动态创建正则表达式时使用构造函数。
ECMAScript 6
开始,创建正则对象或用工厂方法,第一个参数可以不为字符串,可以传递正则表达式字面量。
new RegExp(/ab+c/, 'gi')
// => /ab+c/gi
new RegExp(/ab+c/g, 'i')
// => /ab+c/i 第二个参数指定修饰符,则忽略字面量的修饰符
new RegExp(/ab+c/g)
// => /ab+c/g
正则表达式语法
修饰符
修饰符 | 含义 |
---|---|
i | 忽略大小写 |
g | 全局匹配,找到所有匹配而不是匹配到第一个停止 |
字符类别 (Character Classes)
用于区分不同类型的字符,如区分字母和数字
字符 | 含义 |
---|---|
. | 匹配任意字符,在字符集中. 将失效,匹配字面量'.' |
\d | 匹配阿拉伯数字,等价于 [0-9] 如:/\d+/ |
\w | 匹配字母数字和下划线,等价于[a-zA-Z0-9_] /\w+/ |
\s | 匹配空白字符,如 /Hello\sWorld/ |
\D | 匹配非阿拉伯数字,等价于 [^0-9] |
\W | 匹配非拉丁字母表中的字母数字和下划线, 等价于[^a-zA-Z0-9_] 如匹配50% 中的% |
边界(Boundaries)
表示行和单词的开始和结尾
字符 | 含义 |
---|---|
^ | 匹配输入的开始,如/^A/ 匹配A apple ,而不是a Apple |
$ | 匹配输入的结束,如/e$/ 匹配A apple ,而不是apple and orange |
\b | 匹配一个零宽单词边界,如 "an noon".match(/\b\w+\b/g); => ['an', 'noon'] |
\B | 匹配一个非零宽单词边界,如"an noon".match(/\B\w+\B/g); => ['oo'] |
字符集合和范围 (Character Sets and Ranges)
字符 | 含义 |
---|---|
[xyz] | 一个字符集合也叫字符组,匹配匹配集合中的任意一个字符,如/[abc]/ 匹配apple 中的a |
[^xyz] | 反义字符集,即xyz 取反,反义字符组,匹配不在该集合中的字符,如/[^abc]/ 匹配apple 中的pple |
[n-m] | n和m 可以是字母或数字,表示字符集的范围,/[abc]/ 等效于/[a-c]/ ,条件n<m n要小于m,如果n>m 则会抛出SyntaxError: Invalid regular expression , 如果n=m 则范围失效 |
[^n-m] | 反义字符范围,匹配未包含在字符范围里的字符 |
分组和反向引用 (Groups and back References)
表示表达式字符的分组
字符 | 含义 |
---|---|
(x) | 匹配x并且捕获匹配项,也称为捕获分组括号,在匹配结果中包含匹配项的子字符串通过[1]...[n] 引用匹配结果子字符串,或通过RegExp 静态属性的$1...$9 引用 |
(?:x) | 匹配x不捕获匹配项,也称为非捕获分组括号,在匹配结果中不包含匹配项 |
\n | 捕获项的引用,n未正整数,获取匹配结果n从1开始 |
捕获组有性能惩罚,如果不需要返回捕获的子字符串,建议使用非捕获组,进行字符串匹配
RegExp.$1-$9
不建议使用,见RegExp.$1-$9 - JavaScript \| MDN
- 捕获分组
"foo bar".match(/(foo)\sbar);
// ['foo bar', 'foo'] 通过 [1]引用捕获结果项
"apple, orange, cherry, peach".match(/apple(,)\sorange\1/)
// ['apple, orange,', ','] 通过\n引用匹配项
- 非捕获结果
"foo bar".match(/(?:foo)\sbar/)
// ['foo bar'] 结果不返回捕获项
量词 (Quantifiers)
匹配的字符或表达式的数量
字符 | 含义 | |
---|---|---|
x* | x重复0或多次,等价于 {0,} | |
x+ | x重复1或多次,等价于 {1,} | |
x? | x重复0或1次,等价于 {0,1} | |
`x | y` | 匹配x或y |
x{n} | x重复n次 | |
x{n,} | x至少重复n次 | |
x{n,m} | x至少重复n次,最大重复m次 |
贪婪与非贪婪模式
- 默认为贪婪模式,尽可能多的匹配,
*+
默认是贪婪的,会找到最大匹配项 - 在量词后面添加
?
,会转变为非贪婪模式,尽可能少的匹配,即找到一个匹配项就停止匹配
字符 | 含义 |
---|---|
x*? | 重复0次 |
x+? | 重复1次 |
x?? | 重复0次 |
x{n}? | 无意义 |
x{n,}? | 重复n次 |
x{n,m} | 重复n次 |
断言 (Assertions)
表示一个匹配在模型条件下发生,断言包括先行断言、后行断言和条件表达式
下列所有断言只匹配x,y不参与匹配
字符 | 含义 |
---|---|
x(?=y) | 正向先行断言,仅匹配被y跟随的x |
x(?!y) | 负向先行断言,仅匹配不被y跟随的x |
(?<=y)x | 正向后行断言,x只有在y后面才匹配 |
(?<!y)x | 负向后行断言,x只有不在y后面才匹配 |
断言示例
// 提取下列字符串中以`ing`结尾的单词,并返回不带`ing`的动词
var text = "climbing, oranges, jumping, flying, carrot"
// (\w+) 捕获组,捕获匹配的动词
// (?=ing) 正向先行断言,筛选以ing结尾的单词
// g 修饰符 全局匹配
text.match(/(\w+)(?=ing)/g);
// => ["climb", "jump", "fly"]
正则方法
test
测试当前正则是否能匹配目标字符串。匹配成功返回true
,匹配失败返回false
,和字符串方法String.prototype.search()
类似
regexObj.test(str)
// 简单校验手机号码
var reg = /^1\d{10}$/
var phone = '18966667877'
reg.test(phone)
exec
在目标字符串中执行一次正则匹配操作
regexObj.exec(str)
和RegExp.prototype.test()
不同的是,RegExp.prototype.exec()
不只是返回匹配结果true/false
,而是返回正则匹配结果的更多信息。
单使用全局修饰符g
标志时,每执行一次exec
,都会更新regex.lastIndex
的值
const regex1 = RegExp('foo*', 'g');
const str1 = 'table football, foosball';
let array1;
while ((array1 = regex1.exec(str1)) !== null) {
console.log(`Found ${array1[0]}. Next starts at ${regex1.lastIndex}.`);
// expected output: "Found foo. Next starts at 9."
// expected output: "Found foo. Next starts at 19."
}
字符串方法
match
检索并返回正则表达式匹配的结果
语法
var resultArray = s.match(regexp)
resultArray
返回的结果取决于正则表达式regexp
是否带修饰符g
,而导致返回结果不同。
- 不带修饰符
g
,则返回结果与RegExp.exec()
的结果一样,只返回第一个匹配子字符串和其捕获组["匹配项字符串", "捕获组子字符串"]
- 带
g
,则返回所有的匹配子字符串,["匹配字符串1", "匹配字符串2", ...]
// 获取下面字符串中的数字
var s = "aa11ad22ca33cc";
s.match(/(\d+)/g); // 带全局修饰符
// ["a11", "a22", "a33"]
s.match(/(\d+)/); // 不带全局修饰符, 等价于 /(\d+)/.exec(s)
// ["11", "11", index: 2, input: "aa11aa22aa33aa", groups: undefined]
matchAll
var iterator = s.matchAll(regexp)
返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。
matchAll
,弥补了match
正则使用g
全局修饰符,不返还捕获项问题。iterator
,该迭代器可转化为二维数组,二维数组里的数组包含捕获组信息和捕获组子字符串。
let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';
var arr = [...str.matchAll(regexp)]
arr[0]
// ["test1", "e", "st1", "1", index: 0, input: "test1test2", groups: undefined]
arr[1]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2", groups: undefined]
search
var index = s.search(regexp)
返回字符串中,正则首页匹配的子字符串的索引,未匹配则返回-1
var str = "hey JudE";
var re = /[A-Z]/g;
str.search(re); // 4, 大小J在字符串中的索引是4
split
var array = str.split(seprator)
seprator
分隔符可以是字符串也可以是正则表达式- 返回风格后的数组
seprator
为''
空字符串,则返回原字符串的每个字符组成的数组
var str = "192:172:112:12"
var str2 = "111+334-90"
var str3 = "abcdefg"
str.split(":"); // ["192", "172", "112", "12"]
str2.split(/[+-]/); // ["111", "334", "90"]
str3.split(''); // ["a", "b", "c", "d", "e", "f", "g"]
replace
str.replace(regexp|substr, newSubStr|function) 详细用法见 String.prototype.replace() - JavaScript | MDN
- 第一个参数可以是正则表达式或字符串,第一个参数匹配的内容会被第二个参数替换
- 第二个参数,用于替换第一个参数在原字符串匹配的结果
返回替换后的字符串,原字符串保持不变
需要全局匹配替换,需要在正则表达式使用修饰符g
调换名字顺序
var name = "John Smith"
var reg = /(\w+)\s(\w+)/
// $1是引用第一个捕获组子字符串 $2是访问第二个捕获组的子字符串
var newname = name.replace(reg, '$2 $1')
// => "Smith John"
第二个参数是函数
function styleHyphenFormat(propertyName) {
// 把匹配的子串转换成小写,并在前面加上连接符‘-’
function upperToHyphenLower(match) {
return '-' + match.toLowerCase();
}
return propertyName.replace(/[A-Z]/g, upperToHyphenLower);
}
// 属性名转换函数
styleHyphenFormat("paddingTop")
// => 'padding-top'
正则应用案例
银行号码添加空格
给银行号码添空格,4位分隔符
6216906123453724904
=>6216 9061 2345 3724 904
替换法
/(\d{4})/g
:给字符串分组,从左往右4个一组str.replace(/(\d{4})/g, '$1 ')
:引用捕获组,在后面添加空格字符串str.replace(/(\d{4}\B)/g, '$1 ')
:如果刚好分组到最后一个字符串,则添加一个非单词边界\B
,使其不匹配
"6216906123453724904".replace(/(\d{4}\B)/g, '$1 ')
// 6216 9061 2345 3724 904
插入法
插入法的难点在于,要匹配到插入位置,找到要用哪个断言,进行加工
- 1、由于要匹配4位数字后面的位置,即空白需要用到后行断言
(?<=y)x
,x
为空白可不写,y
为4位数字吗,正则雏形如下:
const str = "6216906123453724904"
const regexp = /(?<=(\d{4}))/g;
str.replace(regexp, ' ')
// "6216 9 0 6 1 2 3 4 5 3 7 2 4 9 0 4 "
- 2、使用'^',标记分组的开始
const str = "6216906123453724904"
const regexp = /(?<=^(\d{4}))/g;
str.replace(regexp, ' ')
// "6216 906123453724904"
- 3、四位数字分组有多组,则在后面加上量词
+
const str = "6216906123453724904"
const regexp = /(?<=^(\d{4})+)/g;
str.replace(regexp, ' ')
// "6216 9061 2345 3724 904"
- 4、如果刚好匹配到最后一个会在结尾添加一个空格,使用
\B
添加非单词边界,限定匹配最后一组
const str = "62169061234537249048"
const regexp = /(?<=^(\d{4})+)/g;
str.replace(regexp, ' ')
// "6216 9061 2345 3724 9048 "
const regexp2 = /(?<=^(\d{4}\B)+)/g;
str.replace(regexp, ' ')
// "6216 9061 2345 3724 9048"
- 5、最后得出
/(?<=^(\d{4}\B)+)/g
正则表达式
千分位分隔符
把整数字符串添加千分位分隔符,如
12193322
=>12,193,322
var str = '12193322'
var reg = /(?=(\B\d{3})+$)/g
str.replace(reg, ',');
// => '12,193,322'
如果不用正则还可以使用Stirng.prototype.toLocaleString('en-US')
快速转换数字为千分位分割的字符串
如果是带小数的字符串,如何用正则转换呢
89887.12211
=>89,887.1211
// 参考
var reg = /\B(?=(\d{3})+(?=\b))(?<=\b(?<!\.)\d*)/g
str.replace(reg, ',')
全国统一社会信用代码
按照编码规则: 统一代码为18位,统一代码由十八位的数字或大写英文字母(不适用I、O、Z、S、V)组成,由五个部分组成:
- 第一部分(第1位)为登记管理部门代码,9表示工商部门;(数字或大写英文字母)
- 第二部分(第2位)为机构类别代码;(数字或大写英文字母)
- 第三部分(第3-8位)为登记管理机关行政区划码;(数字)
- 第四部分(第9-17位)为全国组织机构代码;(数字或大写英文字母)
- 第五部分(第18位)为校验码(数字或大写英文字母)
var a = /[^_IOZSVa-z\W]{2}\d{6}[^_IOZSVa-z\W]{10}$/g;
var b ="91440101739737166A"
console.log(a.test(b))
参考链接:www.cnblogs.com/MyOceansWeb…
规则:参考链接: www.bbsmax.com/A/GBJreMxRz…
姓名
- 中文姓名包括小数民族
/^[\u4E00-\u9FA5\uf900-\ufa2d·s]{2,20}$/
- 中文姓名+英文姓名
/^[\u4E00-\u9FA5A-Za-z\s]+(·[\u4E00-\u9FA5A-Za-z]+)*$/
兼容性问题
Safari 浏览器不支持断言查找(前瞻后顾),会出现如上错误。 如果正则表达式中包含零宽断言的话 , 在安卓手机上正常 , 但是在ios上会报错