JavaScript 正则匹配中英文姓名

工作中突然想给姓名输入框加入正则匹配的验证,以防止用户输入的姓名比网络昵称还奇葩,结果还真费了不少功夫。

首先我们确认规则:

①中英文文字不能混杂

②汉字不能夹杂任何特殊字符和空格(如果要兼容少数民族的姓名的话可以参考英文的匹配写法)

③英文只能夹杂空格,且不能在开头结尾

中文的名称规范很简单:

let reg = /^[\u4e00-\u9fa5]+$/;

英文的名称规范就相对复杂了,因为空格不能在开头结尾,且不能连续。所以我们可以换个角度思考:

英文名称就是一个存储着多个纯字母字符串的数组,然后用空格连起来。因此,除最后一段字母以外,其他所有字母串都默认紧随一个空格,最后必须以字母结尾。因此思路为:

①匹配多个字母,并以空格结尾,匹配0次或多次

②结尾匹配多个字母

代码如下:

扫描二维码关注公众号,回复: 9041368 查看本文章
let reg = /^([a-zA-Z]+\s)*[a-zA-Z]+$/;

当然,我们也可以要求用户的姓名必须以大写字母开头:

let reg = /^(\b[A-Z][a-z]*\s)*\b[A-Z][a-z]*$/;

考虑到本人作为一名中国人,对老外的起名习惯不太了解,所以“限制大写开头”的规则并没有实际使用,还是使用了比较稳妥的“不限制大小写”的版本,没准用户也是个痴迷驼峰命名法的程序员呢(笑)。

综合两个正则规则,我们的用户姓名限制条件写为:

let reg = /(^([a-zA-Z]+\s)*[a-zA-Z]+$)|(^[\u4e00-\u9fa5]+$)/;

 另:这里标记我发现的一个坑。

我曾设想英文的匹配规则如下:

“匹配多个字母串,以字母开头,结尾的空格可有可无,整个字符串最后必须是一个字母。”

写法如下:

let reg = /(^([a-zA-Z]+\s?)*[a-zA-Z]$)/; // 存在缺陷,请勿使用

进行测试:

console.log(reg.test('Ahsflu Aaif Adfssafea'));
// true

测试结果证明,该匹配规则可以验证英文姓名。 

那么问题来了,缺陷在哪里呢?我当时自信地以为找到了不错的匹配规则,但是进行进一步测试的时候发现,这个正则匹配偶尔会卡住!

我们来测试一下:

let reg = /(^([a-zA-Z]+\s?)*[a-zA-Z]$)/;
console.time('time');
console.log(reg.test('Ahsflu Aaif Adfssafea'));
console.timeEnd('time');
console.time('time');
console.log(reg.test('Ahsflu Aaif Adfssadsaffea'));
console.timeEnd('time');
console.time('time');
console.log(reg.test('Ahsflu Aaif Adfssadsdsfaaffea'));
console.timeEnd('time');
console.time('time');
console.log(reg.test('Ahsflu Aaif Adfsaffea '));
console.timeEnd('time');
console.time('time');
console.log(reg.test('Ahsflu Aaif Adfsadsfsdafasdffea '));
console.timeEnd('time');

显示结果如下:

true
time: 8.811ms
true
time: 0.694ms
true
time: 0.707ms
false
time: 1.921ms
false
time: 1192.601ms

注意到了吗?当测试的字符串结尾有空格的时候,整个正则匹配需要的时间随着字符串长度增加而呈现爆炸式增长。很明显这与正则表达式中的'\s?'这个表示可有可无的空格有关。

我的猜测是:由于正则匹配默认采用“贪心”模式,当结尾为空格时,正则匹配根据第一项' ([a-zA-Z]+\s?)* '可以正确的匹配到结尾,但是却发现下一项' [a-zA-Z] '匹配失败了,由于第一项最后的*(匹配1至多个)原则,匹配存在多种可能,程序尚不能判定结果为true还是false,所以必须推倒重来,进行多次尝试。在程序尝试完所有可能的情况之后,程序终于判定字符串不合法,才输出false。

实际情况是:只要我们删掉'\s?'中的'?'指令,这个bug就不复存在了。

这件事情告诉我们在正则匹配中要谨慎使用'?',正如它的含义“可有可无”一样,被'?'形容的规则可以存在于范围内任何可能的地方,这对于死板的程序来说无疑是一个噩梦。

另外,连续使用console.time()方法统计时间的时候,第一项往往会需要更长的时间(分配内存什么的),console.time()统计的时间包括自身。所以即便是统计一个空的语句段,第一次console.time()方法也会比后续方法多出几倍甚至几十倍的时间。

发布了19 篇原创文章 · 获赞 0 · 访问量 1458

猜你喜欢

转载自blog.csdn.net/Z_ammo/article/details/103436589
今日推荐