最近在弄Javascript,有一个需求是需要自定义公式。
而仅仅用公式是不行的,还要支持自定义函数。
那么问题来了,怎么匹配函数呢?
我的方式是用正则表达式。
例如有如下函数:
{ 'Name':'DateDiff',
'Desc':'计算从 StartDate 到 EndDate 之间的时间单位(Unit)个数, Unit 可以为:"y/m/d/h/mi/s",依次代表 "年/月/日/时/分/秒"',
'Returns':'Int 时间单位个数(不包含 EndDate )',
'Format':'DataDiff(StartDate,EndDate,Unit)',
'Regexp':'/DateDiff\((.*?),(.*?),([ymdhs]|mi)\)/i',
'KeyPos':'0,1',
}
这个函数在很多语言里面都有,我就不做过多解释了。
P.S.:这里为了简便起见,前两个参数我仅仅用 .*? 来代替了,实际上不是这样的,需要判断参数的格式是否符合规范。
Js中使用
(Regexp).test(SomeString)
就可以匹配了,看是否命中。
因为这个函数的定义是从数据库中读取的,得出来的Regexp是一个字符串,所有还要用eval转换一下才能成为正则表达式匹配字符串。
变成这样:
(eval(Regexp)).test(SomeString)
就是这个小转换,问题来了。
在实际测试的过程中,产生了如下结果:
var RegexpStr = '/DateDiff\((.*?),(.*?),([ymdhs]|mi)\)/i';
console.log( (eval(RegexpStr)).test('DateDiff(1,2,3)') );
console.log( (eval(RegexpStr)).test('DateDiff(1,2,d)') );
console.log( (eval(RegexpStr)).test('DateDiff(1,2,d3)') );
console.log( (eval(RegexpStr)).test('DateDiff(1,2,daa43523452345)') );
输出:
false
true
true
true
哭笑不得。
第三个参数只要匹配命中第一个字符串,就认为是命中的,后面无论接上什么,都会命中……
如果直接使用正则字符串,就不会出现这个问题。
这个暂时没有找到解决的办法,只能前台判断一次之后后台再判断一次。
Python中无此问题,因为正则字符串无需eval转换。
------------------------------------------------------
2016-05-09更新
话说,这其实不是bug啦,只是我正则写得不对。
要匹配形如 DateDiff(1,2,d) 的字符串,正则应该这么写:
DateDiff\(.*?,.*?,([ymdhs]|(mi))\)
之前少了两个小括号,匹配的时候就有点问题了。
匹配的时候改用exec就更容易发现问题。
而且,把字符串转化成正则字符串也不建议用eval,而应该 new 一个RegExp会比较好。
例如:
var Parren = new RegExp('DateDiff\\(.*?,.*?,([ymdhs]|(mi))\\)', 'i');
不知道你留意到没有,括号是需要“额外”转义的。
因为如果不加 \\ ,那么结果 parren 就会是 “/DateDiff(.*?,.*?,([ymdhs]|(mi)))/i”
少了斜杠,则此表达式永远无法匹配命中。
更新过后,上述正则表达式的测验脚本如下:
var RegexpStr = 'DateDiff\\(.*?,.*?,([ymdhs]|(mi))\\)'; var Parren = new RegExp(RegexpStr, 'i'); console.log(eval('/'+RegexpStr+'/i'),Parren) console.log( (/DateDiff\(.*?,.*?,([ymdhs]|(mi))\)/i).exec('DateDiff(1,2,3)') ); console.log( (/DateDiff\(.*?,.*?,([ymdhs]|(mi))\)/i).exec('DateDiff(1,2,d)') ); console.log( (/DateDiff\(.*?,.*?,([ymdhs]|(mi))\)/i).exec('DateDiff(1,2,d3)') ); console.log( (/DateDiff\(.*?,.*?,([ymdhs]|(mi))\)/i).exec('DateDiff(1,2,daa43523452345)') ); console.log( (eval('/'+RegexpStr+'/i')).exec('DateDiff(1,2,3)') ); console.log( (eval('/'+RegexpStr+'/i')).exec('DateDiff(1,2,d)') ); console.log( (eval('/'+RegexpStr+'/i')).exec('DateDiff(1,2,d3)') ); console.log( (eval('/'+RegexpStr+'/i')).exec('DateDiff(1,2,daa43523452345)') ); console.log( Parren.exec('DateDiff(1,2,3)') ); console.log( Parren.exec('DateDiff(1,2,d)') ); console.log( Parren.exec('DateDiff(1,2,d3)') ); console.log( Parren.exec('DateDiff(1,2,daa43523452345)') );
测验结果:
/DateDiff\(.*?,.*?,([ymdhs]|(mi))\)/i /DateDiff\(.*?,.*?,([ymdhs]|(mi))\)/i /*这一行的括号及斜杠都被csdn吞了,各位测验的时候注意一下。*/
null
["DateDiff(1,2,d)", "d", undefined, index: 0, input: "DateDiff(1,2,d)"]
null
null
null
["DateDiff(1,2,d)", "d", undefined, index: 0, input: "DateDiff(1,2,d)"]
null
null
null
["DateDiff(1,2,d)", "d", undefined, index: 0, input: "DateDiff(1,2,d)"]
null
null