JavaScript正则表达式使用
作者的话
正则表达式在日常工作中真的非常有用,如果想要用复杂的逻辑处理一大段字符串,那么你必须学会使用正则表达式。
本文会通过很多个对字符串使用复杂逻辑处理的例子,带领大家稍微学会正则表达式,但真的想要学好使用正则表达式,一定要在日常生活中经常使用正则,不要再使用循环、splice等方法处理字符串!
另外,本文不会贴上冗长的正则的一些语法、元字符、运算符优先级一类的东西,所以我希望你有这方面的知识,但是如果没有的话也不需要现在去学,我会在使用的例子中详细讲解我所使用的每一个符号的意义
还有,请不要嫌我啰嗦。对同样的字符串可以使用不同的正则,也就是说我本文一些例子题解并不是唯一答案,对于正则来说并不需要唯一答案,只要你写出来,能够正常使用那就够了!
JavaScript 正则的使用
RegExp 对象
首先,我们要使用JavaScript中的正则,那我们必须要构建正则对象,也就是RegExp
。
有三种构建方式。
/pattern/flags
new RegExp(pattern [, flags])
RegExp(pattern [, flags])
pattern
:你书写的正则表达式。
flags
:标志,可以不指定。详细参数
正则常用方法
这一节会介绍我们在使用正则处理字符串的时候经常使用的方法。如果不了解请不要跳过,耐心一点。
RegExp.prototype.exec()
exec()
方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null
。
参数
regexObj.exec(str)//str要匹配正则表达式的字符串
返回值
着重介绍一下返回值。
如果匹配成功,
exec
() 方法返回一个数组,并更新正则表达式对象的属性。返回的数组将完全匹配成功的文本作为第一项,将正则括号里匹配成功的作为数组填充到后面。如果匹配失败,exec() 方法返回
null
。
对象 | 属性/索引 | 描述 |
---|---|---|
result | [0] | 匹配的全部字符串 |
[1], ...[*n*] | 括号中的分组捕获 | [1] = Brown[2] = Jumps |
index | 匹配到的字符位于原始字符串的基于0的索引值 | 4 |
input | 原始字符串 | The Quick Brown Fox Jumps Over The Lazy Dog |
re | lastIndex | 下一次匹配开始的位置 |
ignoreCase | 是否使用了 “i ” 标记使正则匹配忽略大小写 | true |
global | 是否使用了 “g ” 标记来进行全局的匹配. | true |
multiline | 是否使用了 “m ” 标记使正则工作在多行模式(也就是,^ 和 $ 可以匹配字符串中每一行的开始和结束(行是由 \n 或 \r 分割的),而不只是整个输入字符串的最开始和最末尾处。) | false |
source | 正则匹配的字符串 | quick\s(brown).+?(jumps) |
使用例
为了不弹射起步,我们先使用它处理一些基本的操作,比如我们查找句号后面的两个汉字。
let str = "迢迢牵牛星,皎皎河汉女。纤纤擢素手,札札弄机杼。终日不成章,泣涕零如雨;河汉清且浅,相去复几许!盈盈一水间,脉脉不得语。"
let pattern = /。([\u4e00-\u9fa5]{2})/g
while((result = pattern.exec(str))!=null){
console.log(result[1])
}
//纤纤
//终日
做了什么?
先看正则表达式,我说下其中每个字符的意义。
首先看g
,这是我们构建RegExp
对象的flags
属性,g
表示我们全局匹配;找到所有匹配,而不是在第一个匹配后停止。(动手能力强的同学先不要着急去掉g
运行)
回头看正则正文,。(some thing)
,我们按层看,首先是。
当然它会匹配一个。
,接着,()
括号代表的意思是,我们不仅匹配里面的东西,而且还要拿到括号中匹配成功的字符串。记住了吗()
表示不仅匹配 而且保存。那么保存在哪?保存在exec()
方法的返回值里面的[1]、[2]、[3]、....
中。
再往下,[\u4e00-\u9fa5]
,[a-z]
表示字符范围在a到z之间,那么我们这里的用法就很明显,表示编码在\u4e00
-\u9fa5
之间的字符。这一般代表汉字范围。
最后{2}
代表{2}
之前的项目要匹配两个。
ok,说完了正则表达式说一下我们用的方法
我们在while
循环中使用了exec
,为什么不会死循环?因为我们使用了g
全局匹配,要记住pattern
是一个正则对象,其内部是有自己的状态的。当我们使用g
模式的时候,exec
会遍历字符串,返回每一个匹配,直到没有匹配返回null
。
result[1]
,还记得我们的()
吗?保存的获取匹配项就存在这里。
RegExp.prototype.test()
test()
方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true
或 false
。
相比exec
方法,test
方法要简单不少。
参数
regexObj.test(str)//用来与正则表达式匹配的字符串
返回值
如果正则表达式与指定的字符串匹配 ,返回
true
;否则false
。
使用例
这一次我们试着看看字符串是否为规范的邮箱格式!
let str1 = "asahichyan33@gmail.com"//我是邮箱
let str2 = "123[456]]]@qq.com"//我不是邮箱
let pattern = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/g
console.log(pattern.test(str1),pattern.test(str2))
//true false
知识点
\w
:匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’。
.
:匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像"(.|\n)"的模式。
+
:匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
*
:匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
String.prototype.search()
search() 方法执行正则表达式和 String
对象之间的一个搜索匹配。
参数
str.search(regexp)//regexp:一个正则表达式(regular expression)对象。如果传入一个非正则表达式对象 obj,则会使用 new RegExp(obj) 隐式地将其转换为正则表达式对象。
返回值
如果匹配成功,则 search()
返回正则表达式在字符串中首次匹配项的索引;否则,返回 -1。
使用例
如果想要获取匹配,我还是比较建议exec()
方法,但如果单纯检测是否含有某一子串,search
方法也是不错的选择,至少我们在语义化上简洁了不少,对吧?
let str = "迢迢牵牛星,皎皎河汉女。纤纤擢素手,札札弄机杼。终日不成章,泣涕零如雨;河汉清且浅,相去复几许!盈盈一水间,脉脉不得语。"
let pattern = new RegExp("盈盈")
console.log(str.search(pattern))//48
pattern = new RegExp("哈哈")
console.log(str.search(pattern))//-1
String.prototype.replace()
replace() 方法返回一个由替换值(replacement
)替换一些或所有匹配的模式(pattern
)后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。
参数
str.replace(regexp|substr, newSubStr|function)
/*
regexp (pattern)
一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
substr (pattern)
一个将被 newSubStr 替换的 字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。
newSubStr (replacement)
用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名。参考下面的使用字符串作为参数。
function (replacement)
一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考下面的指定一个函数作为参数。
*/
使用例
replace
是一个大杀器,毕竟我们在处理字符串时,不是只查找含有子串,更多的是需要我们替换子串。replace
方法能使我们以更清楚的逻辑处理字符串。
//在本例中,我们要将下划线、@、+转单驼峰,如user_model->userModel
let str = "__make@by_id__to+_name"
let pattern = /[@|_|/+]{1,}(\S)/g
console.log(str.replace(pattern,(match,s1)=>{
return s1.toLocaleUpperCase()
}))
///MakeByIdToName
知识点
/+
因为我们要匹配+
,但是+
本身是正则中的元字符,所以我们要将其转义。
{1,}
匹配1次及以上。
(match,s1)=>{}
箭头函数,ES6语法。
s1
有人好奇它是哪来的吗?这其实就是我们用()
匹配获取的。
正则高级技巧
捕获括号(x)
模式
/(foo) (bar) \1 \2/
中的 ‘(foo)
’ 和 ‘(bar)
’ 匹配并记住字符串 “foo bar foo bar” 中前两个单词。模式中的\1
和\2
表示第一个和第二个被捕获括号匹配的子字符串,即foo
和bar
,匹配了原字符串中的后两个单词。注意\1
、\2
、…、\n
是用在正则表达式的匹配环节,详情可以参阅后文的 \n 条目。而在正则表达式的替换环节,则要使用像$1
、$2
、…、$n
这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')
。$&
表示整个用于匹配的原字符串。
非捕获括号(?:x)
匹配 ‘x’ 但是不记住匹配项。这种括号叫作非捕获括号,使得你能够定义与正则表达式运算符一起使用的子表达式。看看这个例子
/(?:foo){1,2}/
。如果表达式是/foo{1,2}/
,{1,2}
将只应用于 ‘foo’ 的最后一个字符 ‘o’。如果使用非捕获括号,则{1,2}
会应用于整个 ‘foo’ 单词。
先行断言x(?=y)
匹配’x’仅仅当’x’后面跟着’y’.这种叫做先行断言。
例如,/Jack(?=Sprat)/会匹配到’Jack’仅仅当它后面跟着’Sprat’。/Jack(?=Sprat|Frost)/匹配‘Jack’仅仅当它后面跟着’Sprat’或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。
后行断言(?<=y)x
匹配’x’仅仅当’x’前面是’y’.这种叫做后行断言。
例如,/(?<=Jack)Sprat/会匹配到’ Sprat ‘仅仅当它前面是’ Jack '。/(?<=Jack|Tom)Sprat/匹配‘ Sprat ’仅仅当它前面是’Jack’或者是‘Tom’。但是‘Jack’和‘Tom’都不是匹配结果的一部分。
正向否定查找x(?!y)
仅仅当’x’后面不跟着’y’时匹配’x’,这被称为正向否定查找。
例如,仅仅当这个数字后面没有跟小数点的时候,/\d+(?!.)/ 匹配一个数字。正则表达式/\d+(?!.)/.exec(“3.141”)匹配‘141’而不是‘3.141’
反向否定查找(?<!y)x
仅仅当’x’前面不是’y’时匹配’x’,这被称为反向否定查找。
例如, 仅仅当这个数字前面没有负号的时候,
/(?<!-)\d+/
匹配一个数字。
/(?<!-)\d+/.exec('3')
匹配到 “3”.
/(?<!-)\d+/.exec('-3')
因为这个数字前有负号,所以没有匹配到。
对于上面这些操作,我们经常使用的一般就是(x)
、(?:x)
、x(?=y)
、(?<=y)x
。
一些使用例子
哦,在这些例子中我使用了一点点TypeScript
语法,如果你会运行,那很好。但是如果你不会运行,那你大可重写一遍这些例子,这样可以更深刻记忆~
例一、模板字符串
/**
* 用属性替换模板字符串
* @param str 模板字符串
* @param obj 替换对象
*/
function tempStr(str:string,obj:object):string{
let p = /\{\$(\S+)\}/g
return str.replace(p,(match,s1)=>{
return obj[s1]
})
}
let str = "My name is {$name},and My path in {$path}!"
let obj = {
name:"于浩岩",
path:"北京 回龙观"
}
console.log(tempStr(str,obj))
例二、下划线转单驼峰
/**
* 下划线转单驼峰
* 例:a_ba___ca->aBaCa
* @param str 要转换的字符串
*/
function codeFormet(str:string):string{
let p1 : RegExp = /_+([a-z])/g
str = str.replace(p1,(match,s1)=>{
return s1.toLocaleUpperCase()
})
return str;
}
console.log(codeFormet("a_ba____ca_a______"))
例三、名字交换
/**
* 名字交换
* 例:“Sam Tom”->"Tom Sam"
* @param str 替换字符串
*/
function alternatelyName(str:string):string{
let p:RegExp = /(\S+)\s(\S+)/g
return str.replace(p,"$2 $1");
}
console.log(alternatelyName("Sam Bob"))
参考
- RegExp - JavaScript | MDN
- RegExp.prototype.test() - JavaScript | MDN
- 正则表达式 - JavaScript | MDN
- 正则表达式 – 元字符 | 菜鸟教程
- String.prototype.search() - JavaScript | MDN
- String.prototype.replace() - JavaScript | MDN