从两个简单的函数了解正则

字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在。而正则表达式是一种用来匹配字符串的强有力的武器,所以正则是程序员必须掌握的强有力工具之一,对任何编程语言都一样。下面从两个简单的函数来看看简单的正则。

第一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
*
* @param {Object} 时间戳 毫秒数
* @param {Object} fmt 日期格式 例如:yyy-MM-dd hh:mm:ss
*/
function formatDate(date,fmt){
//年单独匹配
if(/(y+)/.test(fmt)){
fmt = fmt.replace(RegExp.$1,(date.getFullYear()+'').substr(4-RegExp.$1.length));
}

//月,日,时,分,秒同样的匹配方式
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};

for(let k in o){
if(new RegExp(`(${k})`).test(fmt)){
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1,(RegExp.$1.length === 1)?str:padLeftZero(str));
}
}

return fmt;
};
/**
* 不足两位数前面添0
* @param {String} str
*/
function padLeftZero(str) {
return ('00' + str).substr(str.length);
}

这是一个时间戳转化成标准日期时间的方法,在日常开发中,一般我们通过请求获取的后台的时间格式都是从1970 年 1 月 1 日至今的毫秒数,即我们说的时间戳,所以这是一个我们经常需要用到的字符串过滤函数。

下面解释一下这个函数:

第一个if语句是单独匹配年份的:

1
2
3
if(/(y+)/.test(fmt)){
fmt = fmt.replace(RegExp.$1,(date.getFullYear()+'').substr(4-RegExp.$1.length));
}

先解释一下上面用到的三个字符串操作的方法:

  • test() 方法用于检测一个字符串是否匹配某个模式。语法:RegExpObject.test(string)
  • replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。语法:stringObject.replace(regexp/substr,replacement),两个参数都是必须的。
  • substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。语法:stringObject.substr(start,length),第一个参数必须,第二个长度可选

所以上面if部分的意思就是:

  1. 如果fmt中含有一个或多个y(正则以“/”开始和结束,“+”表示一个或多个),开始的字符,则匹配成功,返回true,if为真;
  2. 如果fmt中不存在一个或多个y开始的字符则返回false,if为假。
  3. if为真时,正则第一次匹配成功的字符串(RegExp.$1表示第一次匹配成功,RegExp.$3表示第三次匹配成功的字符,以此类推至99)替换为date.getFullYear()获取的转化为字符串的年份。并且利用substr()根据匹配年份的的字符串长度截取从末尾开始的长度的年份。(例如匹配为yyyy则返回 2018,yy则返回18)。

后面的语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//月,日,时,分,秒同样的匹配方式
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};

for(let k in o){
if(new RegExp(`(${k})`).test(fmt)){
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1,(RegExp.$1.length === 1)?str:padLeftZero(str));
}
}

这里是利用一个对象o存储接下来需要匹配的月,日,时,分,秒。因为他们格式都是一样的。这里解释一下for in 循环里面的意思:

  1. 遍历o对象,每次遍历new一个正则对象,这里利用es6的语法(${k})传递对象值,下面以第一个M+月份为例,其中new RegExp((${k}))类似于之前的/(M+)/.test(fmt)。
  2. 如果fmt中存在一个或多个M开始的字符串,则匹配成功;定义一个字符串str记录需替换匹配成功字符串的值。
    3.fmt等于时间戳计算得月份的值(这里利用三位运算符,如果月份不足两位数,利用方法前面补0)替换第一次匹配成功的字符串。

第二个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 解析url参数
* @example ?id=12345&a=b
* return Object {id:12345,a:b}
*/
function urlParse(){
let url = window.location.search;
let obj = {};
let reg = /[?&][^?&]+=[^?&]+/g;
let arr = url.match(reg)
// ['?id=12345','&a=b']
if(arr){
arr.forEach((item) => {
// 用substring取第一个以后的字符,即删除第一个字符,并且用=分割
let tempArr = item.substring(1).split("=");
let key = decodeURIComponent(tempArr[0]);
let val = decodeURIComponent(tempArr[1]);
obj[key] = val;
})
}
return obj;
};

这是一个分割url参数并转发为对象的函数。日常开发我们也经常遇到需要获取url上面参数的问题。

照例先解释一下上面用到的三个字符串操作的方法:

  • match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。语法:stringObject.match(regexp)这里的regexp也可以是字符串,我们这里用的是正则。
  • substring() 方法用于提取字符串中介于两个指定下标之间的字符。语法:stringObject.substring(start,stop),第一个start必须,stop可选。返回从 start 处到 stop-1 处的所有字符。注意:只有一个参数的话和substr() 方法具有相同功能,就是截取字符串当前下标以后直到字符串最后的字符串片段。
  • decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。语法:decodeURIComponent(URIstring)。与其类似的decodeURI() 函数可对 encodeURI() 函数编码过的 URI 进行解码。

下面解释一下这个函数:

  1. 通过window.location.search方法获取url?开始的部分
  2. 新声明一个空对象,用于存储最后需要返回的url中参数对象
  3. 定义一个正则,表示匹配字符集[?&]中任意字符,然后匹配非字符集[^?&]中的元素(^表示非),加上”=”,继续匹配一个或多个非字符集[^?&]中的元素,全局匹配(g结尾表示可以全局匹配,这里的可以表示不同的情况下表现不同,这里你只要知道:对于String对象的match方法,不加入g,也只是返回第一个匹配,一直执行match方法也总是返回第一个匹配,加入g,则一次返回所有的匹配)
  4. 用数组arr存储match() 方法获取的全局匹配数组
  5. 遍历arr数组,利用split() 方法分割和 encodeURIComponent()方法解码存储到obj对象
  6. 返回对象obj

以上就是日常开发中利用到正则的两个简单的函数,希望从这两个函数中,你可以简单了解一下正则,并通过查找其他书籍和资料深入学习正则。



完~