js正则分析案例——以JSON格式校验为例
js正则分析案例——以JSON格式校验为例
[TOC]
缘起
最近在研究javascript中对各种数据类型与格式的判断,以及各种第三⽅库提供的字符串处理⽅法,发现有⼤量的地⽅运⽤了正则,并且有些正则及其复杂。对于上层应⽤开发⼈员来说,正则可能⽤到的地⽅并不是太多,最常⽤的⽆⾮就是表单验证,⽽那些常见的表单验证正则⽹络上也是⼀搜⼀⼤堆,⾃然不需要⾃⼰再去构建。
但是,作为⼀名开发⼈员,肯定不⽢于⼀直做⼀个“⼯具⼩⼦”,⽽希望⾃⼰也能写出⼀些更底层的代码,甚⾄是开发⼀些底层的库来供别⼈使⽤,所以正则就成为了⼀个绕不过去的坎。
当我们决定⾃⼰写正则的时候,总是会先去看看前辈们是如何写的,毕竟站在巨⼈的肩膀上才能站得更⾼。所以解读正则就是⼀个不可获取的技能。
今天,我们就拿⼀个校验JSON格式的正则来⽰范⼀下如何拆解复杂的正则表达式,以⽅便我们更好地阅读和理解⼤神们的源码。BTW,也可以借机更深⼊的了解下JSON规范。
JSON
JSON是⼀种被⼴泛应⽤于各种编程语⾔的数据交换格式,下⾯是JSON格式的官⽅说明,阅读它有助于更好地理解本⽂内容:
建议对照这份说明⾷⽤本⽂,会更加⾹甜。
另外,也可以同时对照javascript正则表达官⽅说明来阅读本⽂:
javascript判断⼀个字符串是否为JSON格式
下⾯这段代码来⾃于⽹络,是使⽤正则判断⼀个字符串是否为JSON字符串:
var isJSON = function (str){
if (/^[\],:{}\s]*$/.place(/\\["\\\/bfnrtu]/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d (?:\.\d*)?(?:[eE][ \-]?\d )?/g, ']').
replace(/(?:^|:|,)(?:\s*\[) /g, ''))) {
return true;
}else{
return false;
}
}
解读过程
为了更直观地理解上⾯这段代码是如何判断JSON格式字符串的,我们准备了⼀段复杂的但合法的JSON字符串,⼀步⼀步来理解上⾯这段代码的正则替换与校验过程。我们准备的JSON数据如下:
{
"array": [
1,
-2,
2.22,
3e12,
-32.1e-12,
[1,2,3],
{
"abc":123.34
}
],
"boolean": true,
"null": null,
"number": 123,
"object": {
"a": "b",
"c": "d",
"e": "f",
"subarray":[1,2,3,4]
},
"string": "Hello World",
"string2":"\\babc\\"def\\fh\\rjkl\\nmn\sss\\tie\\uABCF"
}
我们将它格式化为字符串:
var str = '{"array":[1,-2,2.22,3e12,-32.1e-12,[1,2,3],{"abc":123.34}],"boolean":true,"null":null,"number":123,"object":{"a":"b","c":"d","e":"f","subarray":[1,2,3,4]} ,"string":"Hello World","string2":"\\babc\\"def\\fh\\rjkl\\nmn\sss\\tie\\uABCF"}';
第⼀步整体分析
上⾯的isJson() ⽅法中,实际上执⾏了以下⼏个步骤:
1. 对字符串执⾏了正则替换,将⼀部分内容替换为@符号:place(/\["\/bfnrtu]/g, '@')
2. 继续对字符串执⾏正则替换,将上⼀步结果的⼀部分内容替换为]号:
replace(/"[^"\nr]*"|true|false|null|-?d (?:.d*)?(?:[eE][ -]?d )?/g, ']')
1. 继续对上⼀步结果执⾏正则替换,将部分内容替换为空⽩符
2. 对上⼀步的结果执⾏了正则校验:/^[],:{}s]*$/.test()
3. 上⼀步test()⽅法返回⼀个布尔值,作为if判断的最终条件,得到结果
第⼆步分步骤解析
1. 替换控制字符为@符号
我们看这个正则:place(/\["\/bfnrtu]/g, '@')
在上⽂的链接的JSON介绍页⾯中,介绍了JSON合法字符串值可接受的控制字符,可参阅该页⾯string格式部分
/\\["\\\/bfnrtu]/g
这条正则代表JSON标准中规定的可接受的特殊字符,由于反斜杠(\)为转义字符,所以我们先将正则⾸尾的斜杠和特殊符号之前的转义去掉,得到:
* \["\/bfnrtu],即 “\” 后⾯加上【"\/bfnrtu】中的其中⼀个字符([]内的为字符组,在正则中代表其中⼀个):
*  \" 双引号
*  \/ 斜杠
*  \\ 反斜杠
*  \b --backspace  后退符号
*  \f--formfeed  换页符号
*  \n--linefeed  换⾏符号
*  \r--carriage return  回车符号
*  \t--horizontal tab  垂直制表符
*  \u--unicode编码
我们对我们准备的那⼀段JSON字符串做同样的操作:
var str1 = place(/\\["\\\/bfnrtu]/g, '@');
console.log(str1);
得到:
{"array":[1,-2,2.22,3e12,-32.1e-12,[1,2,3],{"abc":123.34}],"boolean":true,"null":null,"number":123,"object":{"a":"b","c":"d","e":"f","subarray":[1,2,3,4]4]},"string ":"Hello World","string2":"@abc@def@h@jkl@mnsss@ie@ABCF"}
可以发现,这段正则替换将原始JSON中 string2的值进⾏了替换,因为它的值中间包含了控制字符:
"string2":"\\babc\\"def\\fh\\rjkl\\nmn\sss\\tie\\uABCF"
// =>
"string2":"@abc@def@h@jkl@mnsss@ie@ABCF"
⼤家⼀定注意到了,上⾯正则分析中是⼀个反斜杠加上⼀个控制字符, ⽽我么原始字符串中缺失两个斜杠加上控制字符,这是因为我们
在JSON字符串中依然需要对控制字符前⾯的反斜杠进⾏转义,所以必须在前⾯再加⼀个反斜杠来承担
转义的⼯作,否则将不会通过校验。⽽s前⾯为什么没有转义呢,因为它不属于JSON规范中的控制字符,它只单纯地表⽰空格。
2. 替换键名和值为右中括号
replace(/"[^"\\\n\r]*"|true|false|null|-?\d (?:\.\d*)?(?:[eE][ \-]?\d )?/g, ']')
js正则表达式判断数字我们将正则部分拆解如下:1.去掉正则格式符(/,/g):
"[^"\nr]*"|true|false|null|-?d (?:.d*)?(?:[eE][ -]?d )?
1. 分⽀结构(即使⽤|隔开的部分),有以下分⽀:
1. "[^"\nr]*"
1. 双引号(")后⾯跟上除了(^在正则中代表“⾮”,即“不是xxx”)【",,n,r】之外的任意字符任意次(*量词在正则中代
表任意次),后⾯再跟⼀个双引号(")这⼀步其实匹配双引号包含的所有内容,⽽我们知道,在合法`JSON`字符串中,
键名是必须⽤双引号包含的,⽽合法值⾥的 string类型也是必须⽤双引号包含起来的。
1. true
2. false
3. null
4. -? d (?: .d*)? (?: [eE] [ -]? d )?【匹配number】
1. 0个【即正数】或者1个负号(-)【即负数】,(?在正则中代表0个或1个)
2. 后⾯跟上1个以上的数字(d),
3. 再跟上零组【有可能没有⼩数部分】或⼀组(括号为分组,?:代表⾮捕获分组)⼀个点后⾯跟上任意个数字
【数字的⼩数部分】,
4. 再跟上指数部分(e或E后⾯跟上数字[可正可负])
2. 所以这⼀步实际上就是把合法的json键名和值(string/true/false/null/number)替换为右中括号
来看看我们准备的JSON字符在这⼀步后被替换为什么样:
var str2 = place(/"[^"\\\n\r]*"|true|false|null|-?\d (?:\.\d*)?(?:[eE][ \-]?\d )?/g, ']');
console.log(str2);
{]:[],],],],],[],],]],{]:]}],]:],]:],]:],]:{]:],]:],]:],]:[],],],]]},]:],]:]}
3. 替换⾏⾸位置、冒号、逗号为空⽩符
replace(/(?:^|:|,)(?:\s*\[) /g, '')
去掉正则格式符,得到:
(?: ^|:|, )(?: s* [ )
有两个⾮捕获分组
第⼀组:^|:|, 即匹配⾏⾸位置(^)或冒号(:)或逗号(,)
第⼆组:s*[ 即匹配任意个(*)空格`(s)`后⾯跟左中括号`([)`,这种组合可能出现1到多次【应对多层嵌套的情况】
最后匹配的就是第⼀组中的其中⼀个符号后⾯跟上第⼆组格式的字符串,⽐如 :[ 或者 ,[这样的
看看我们的JSON字符串在这⼀步后变成了什么样:
var str3 = place(/(?:^|:|,)(?:\s*\[) /g, '');
console.log(str3);
{]],],],],]],],]],{]:]}],]:],]:],]:],]:{]:],]:],]:],]],],],]]},]:],]:]}
4. 使⽤test()⽅法校验
/^ [\],:{}\s]* $/.test(str3)
看正则部分: /^ [ ] , : { } s ]* $/
即⾏⾸后⾯跟上【右中括号(注意转义),逗号,冒号,左⼤括号,右⼤括号,空格】中的任意⼀个字符任意次,然后是⾏尾
根据这个规则,我们的JSON字符串成功通过了校验。
本⽂就到这⾥, 希望可以对您有所帮助。
本⽂由博客⼀⽂多发平台 发布!