本文发布在个人博客一道小小的题目引起对javascript支持正则表达式相关方法的探讨
许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文连接及做者。javascript
之前对于正则是很是害怕的,由于看不懂和学不会。但最近项目中频繁的使用到了正则,所以强迫本身去学习了解,慢慢的体会到了他的魅力与强大。固然学习正则初入门的时候有些枯燥难懂,但越学越以为轻松。本文不许备说关于正则自己的事儿,而是说一说关于javascript中关于正则的几个方法中被不少人忽略的地方。java
说到正则,不少人都是从抄到改到本身写,这个过程可能有时候很漫长。如一些工具能帮助你快速分析和学习正则,那么学习的过程你确定要轻松得多。下面我推荐两个我常用的正则在线可视化工具,正则可视化工具图解符合铁路图规律(其实不明白什么是铁路同样很容易看懂,只是一些细微的地方和咱们的常规思惟有点差异)。git
这道题目是在群里平常闲聊时,公司同事抛出来的,具体是出自哪里本人没去考察。先先说说题目:github
写一个方法把一个数字末尾的连续0变成9,如1230000变成1239999正则表达式
一道很简单的题目,直接正则就能搞定,也许你会写:segmentfault
function zoreToNine(num){ return (num + '').replace(/0/g,9); } //或者 function zoreToNine(num){ return (num + '').replace(/[1-9]0+$/,9); }
这也是此题的陷阱所在,按照上面的方法,1023000就会被转化成1923999,这样是不符合要求的,因此改进一下:数组
function zoreToNine(num){ return (num + '').replace(/[1-9]0+$/,function($1){ return $1.replace(/0/g,9); }); } zoreToNine(1223000); //1223999 zoreToNine(1023000); //1023999
关于这个问题的解决方案@微醺岁月同窗提供了一种,位置匹配的方法,简单了不少,厉害!函数
"12300100000".replace(/0(?=(0+$)|\b)/g,9); //12300199999
固然解决问题的方法不少,不必定非要用正则,还彻底可使用纯算术的方法实现,你们有兴趣能够尝试,闲话少说进入此次的主题:javascript
支持正则表达式相关方法,注意并非正则对象的方法。
上述方法使用了正则,有趣的是在回调函数里有一个$1,这个$1究竟是什么?全部的匹配规则匹配后都有$1这个变量么?...一连串的问题,之前我历来没有去追探过,趁着昨个比较空闲,去追探了一番,并在今天整理了一下,写下此文记录。工具
javascript
中正则对象有三个方法:test
、exec
和compile
,可是这次的主角并非它们!咱们讨论的是可以使用正则表示的相关方法:search
、match
、replace
和split
,注意它们都是String
对象的方法,使用它们必需要是String
类型.学习
replace
是一个用于替换字符串的方法,虽然看似简单,可是它隐藏的机关也是经常被人忽略。具体分析一下它的特色:
它接收两个参数 无反作用不影响原始变量 返回被改变的字符串(必定是字符串类型)
定义一些变量,方便全文取用。
let a = '12309800', b = '12309800[object Object]', b = '12309800{}';
在通常状况,rule参数通常是正则、字符串、数字。
若是是字符串,将会在匹配到第一个符合条件的目标,结束方法;
若是是正则,则按照正则的规则进行匹配
//匹配第一个0替换成5 a.replace(0,5); //'12359800' //匹配全部的0替换成5 a.replace(/0/g,5); //'12359855'
在通常状况,replacement参数是字符串、数字、者回调。
当参数rule为正则,而且正则至少包含有一对完整的()
时,若是replacement
包含有$的字符串,那么对于$n
(n为大于0的整数,n的长度取决于正则中括号的对数),会被解析成一个变量。可是也仅仅只是做为一个变量,没法在字符串中进行计算,此时更相似特别的字符串模板变量。
通常状况下,$n
中n的长度取决于正则中括号的对数,$1表示第1对括号匹配的结果,$2表示第2对匹配的结果...在正则全部的括号对中,左括号出如今第几个位置(或者说从左往右),则它就是第几对括号,以此类推。姑且咱们把这种规则成为正则匹配分割规则
(ps:这彻底是我本身取的一个名字,方便文章后面使用和记忆)。
a.replace(0,'$0'); //'123$09800' a.replace(/00/g,'$0'); //'123098$0' a.replace(/[1-9]0+$/,'$1'); //'12309$1' a.replace(/([1-9](0+$))/,'$1'); //'12309800',此时$1为[1-9](0+$)匹配到的内容,$2为0+$匹配到的内容 a.replace(/([1-9])(0+$)/,'$1'); //'123098',此时$1为[1-9]匹配到的内容,$2为0+$匹配到的内容 a.replace(/([1-9])(0+$)/,'$1*$2'); //'123098*00',此处的$1和$2不会安照期待的状况进行乘法计算,要进行计算能够用回调
请注意:虽然目前参数replacement中携带有$n仍然能正常使用,可是这种方式已经不被规范所推荐,更应该使用回调来完成这个操做。这一点谢谢@lucky4同窗的指出
若是正则中包含有全局匹配标志(g),那么每次匹配的都符合上述规则
先看例子:
a.replace(/[1-9]0+$/,function(){ console.log(arguments); //["800",5,"12309800"]、 }); a.replace(/([1-9])0+$/,function(){ console.log(arguments); //["800","8",5,"12309800"] }); a.replace(/([1-9])(0+$)/,function(){ console.log(arguments); //["800","8","00",5,"12309800"] }); a.replace(/(([1-9])(0+$))/,function(){ console.log(arguments); //["800","800","8","00",5,"12309800"] });
回调函数的arguments
数组部分组成:[完整匹配的字符串,$1,$2,...,$n,匹配的开始位置,原始字符串],$1...$n
表示每一个括号对的匹配,规则和前面的相同。
因此有一下规律:
let arr = [...arguments], len = arr.length; (len >= 3) === true; arr[0] = 完整匹配的字符串; arr[len-2] = 匹配的开始位置; arr[len-1] = 原始字符串;
注意:除了匹配的开始位置是Number
类型外,其他的都是String
类型
若是参数类型不是上述两种状况,会发生什么呢?看看下面的例子:
a.replace(0,null); //123null9800 a.replace(0,undefined); //123null9800 a.replace(0,[]); //1239800 a.replace(0,Array); //1230,3,123098009800 b.replace({},5); //123098005 c.replace({},5); //'12309800{}' a.replace(0,{}); //123[object Object]9800 a.replace(0,Object); //12309800
由上面的例子能够看出,若是非正则也非字符串,则有如下规则:
`null`变量,则会转换成`'null'`字符串; `undefined`变量,则会转换成`'undefined'`字符串; `[]`变量,则会调用`join()`方法转换成字符串,默认以`,`分割,值得注意的是空数组将会被转换成空字符串(没有任何字符),一般会被匹配源字符串的开始位置(默认开始位置为空字符串); 'Array'变量,则会先转成成一个匹配的数组,形如`[完整匹配的字符串,$1,$2,...,$n,匹配的开始位置,原始字符串]`,而后对它调用`join()`方法转换成字符串,默认以`,`分割; `{}`变量,则会调用`Object.protype.toString.call()`方法把`{}`转换成`[object Object]`; `Object`变量,则貌似什么都没作
虽然能够传入这些非正常参数,但大多数状况下这些类型的参数对实际是毫无心义的,因此不建议传入以上类型的参数。同上面的正则匹配分割规则
同样,为了方便使用称呼,姑且我把上面的转换规则称为正则匹配参数转换规则
match
方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
该方法相似indexOf
和lastIndexOf
,可是它返回指定的值,而不是字符串的位置;
参数的传递除了常规的正则和字符串之外,其他全部类型的参数都会按照上述的正则匹配参数转换规则
转换成字符串形式来匹配。
返回值根据传入的参数类型和规则的不一样,返回的内容不一样,但整体来讲,它是返回一个对象,而不是索引,若是没匹配到任何符合条件的字符串,则返回null
。
若是匹配规则是一个非全局匹配规则,那么,它此时的返回值是一个伪数组对象(likeArr),形如:[一个展开的匹配到的字符串数组, 匹配到的字符串位置, 原始字符串],它有以下规律:
var likeArr = a.match(regex); likeArr[0] = 匹配到的字符串; likeArr[1...n] = 正则匹配分割规则匹配的字符串; likeArr.index = 匹配到字符串的位置 likeArr.inupt = 原始字符串
看例子:
a.match(/[1-9]0+$/); //[0:'800',index:5,input:'12309800'] a.match(/([1-9])0+$/); //[0:'800',1:'8',index:5,input:'12309800'] a.match(/[1-9](0+$)/); //[0:'800',1:'00',index:5,input:'12309800'] a.match(/([1-9])(0+$)/); //[0:'800',1:'8',2:'00',index:5,input:'12309800']
若是匹配规则是一个全局匹配规则(正在携带有g标志),那么,它此时的返回值是一个数组对象(arr),形如:[匹配到的字符串数1,匹配到的字符串数2,匹配到的字符串数3];
看例子:
a.match(/[1-9]0/); //[0:'30',index:2,input:'12309800'] a.match(/[1-9]0/g); //[0:'30',1:'80']
search
方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。stringObject
中第一个与rule
相匹配的子串的起始位置。若是没有找到任何匹配的子串,则返回-1
。
注意:
search
方法不执行全局匹配,它将忽略标志g
。regexp
的lastIndex
属性,老是从字符串的开始进行检索,这意味着它老是返回stringObject
的第一个匹配的位置一样,search
能够传入任何参数类型,它会遵循正则匹配参数转换规则
进行转换
这个方法就不用多说,很经常使用的字符串分割方法。
第二个参数的做用就是限制返回值的长度,表示返回值的最大长度
固然,它依然能够传入任何参数类型,会遵循正则匹配参数转换规则
进行转换
有一段加密的后的密码,咱们须要分离出字符串'12a344gg333tt445656ffa6778ii99'中的前三组数字,经过某种计算才能得出正确的密码
'12a344gg333tt445656ffa6778ii99'.split(/[a-zA-Z]+/g,3);//['12','334','333']
写了这么多,忽然发现之前仅仅是在用这些方法,了解得很不够深刻。越是学习才发现其中的奥秘!学无止境,与诸君共勉!以上内容若有错误之处,但愿诸君不吝指出!