【阅读整理】正则表达式 - 基础篇

991 阅读4分钟

前言

大家学生时代在数学卷子上的填空题,可能会遇到这样的找规律题目,比如最简单的等差数列:

已知数列:1,5,9,13,17…,求该数列的表达式是________
答案是显而易见的:4*n-3 (n为整数)

我刚接触正则表达式的时候,很自然地想到了这里。

我们构建的正则表达式,比如 /c{2,5}ode/ 很类似于数列的表达式,要去匹配的字符串比如 "code""ccode""cccode" 很类似于数列中的数字。

所以正则表达式是对目标字符串们的共性,抽象出来的表达!

正则表达式的身影出现在各种开发语言中…这里,我们走入 JavaScript 的正则世界。在开始之前,我给大家分享两个正则的在线工具:

Let’s Go!!!

正则匹配的内容

正则匹配的内容有两部分,一个是字符,一个是位置。

字符很好理解,比如 "code"中的co;位置呢,指的是相邻字符之间的空隙——co之间、od之间,另外还有特殊的像开始与结束:^$,只有一个边是字符。

这里画了一张匹配字符和位置的小结:

Demo

匹配字符demo:

  • 匹配颜色16进制:这里有横向匹配,使用了量词 {6}{3};有纵向匹配,使用了字符组 [0-9A-Fa-f];有多选分支,使用了管道符|
  • 匹配html中的id值-方法1:比如 '<div id="container" class="main"></div>' 要找出 id="container"。 这里在量词后加?是开启了惰性匹配,惰性匹配是尽可能吃得少,而默认的贪婪匹配是尽可能吃得多,试试链接中去掉问号的结果👀
  • 匹配html中的id值-方法2:(条条大路通罗马)这里用排除字符组明确排除双引号之间不能再有双引号

匹配位置demo:

需求:数字的千位分隔符表示,比如输入"12345678",转为12,345,678。我们会用到str#replace

var result = "123456789".replace(/.../)/g, ',')
console.log(result);

正则们:

  • 数字的千位分割符表示-方法1:这里用了结束位置符$,从数字末尾开始;用了Positive lookahead (?=(\d{3})),找到每3位数字后面的位置;用了Negative lookahead (?!^),排除,123,456,789这样的情况;
  • 数字的千位分割符表示-2:这里用Postive lookbehind (?<=\d) 代替了上一个例子中的(?!^),这里有一个细节是当多个位置放在一起时,看作对这个位置的多个限制条件。
  • 数字的千位分割符表示-3:这里用了单词和非单词边界,可以匹配多个数字。

工具:括号

括号除了提高优先级以外,还有一个重要的功能:分组。以便我们能引用它:

  • 在JavaScript中引用
  • 在正则表达式里引用

简单demo:在JavaScript中引用

注意这里的(?:p)是非捕获分组,这样的分组不能被捕获,也不能被引用

//模拟trim方法:在JavaScript中引用分组
function trim(str) {
	return str.replace(/^\s*(.*?)\s*$/g, "$1");
}
console.log( trim("  foobar   ") );

//将单词首字母大写:在JavaScript中引用分组
function titleize(str) {
	return str.toLowerCase().replace(/(?:^|\s)\w/g, function(c) {
		return c.toUpperCase();
	});
}
// console.log( titleize('My name is harden') );

简单demo:在正则表达式里引用

保证分割符前后一致

var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false

JavaScript API使用注意事项

一共有6个API:

  • str#search:返回首次匹配的位置,未找到返回 -1
  • str#split
  • str#match:有 g ,返回的是所有匹配的内容;没有 g ,返回,首个匹配结果 + 捕获组数据(没有,返回 null )+ 匹配结果开始位置 + 搜索的字符串;没有匹配返回 null
  • str#replace
  • regex#test:有匹配返回 true ,没有匹配返回 false
  • regex#exec:返回 当前匹配结果 + 捕获组数据(没有,返回 null )+ 匹配结果开始位置 + 搜索的字符串

和正则有关的操作(罗列推荐使用的方法):

  • 验证:regex#test
  • 切分:str#split
  • 提取(提取括号中内容):str#matchregex#exec 差不多,另外str#replace在参数函数中获取数据也方便;
  • 替换:str#replace

注意事项:

  • str#searchstr#match 都支持字符串和正则作参数,在字符串作参数时会自动字符串转正则。遇到元字符要转义:
var string = "2017.06.27";

console.log( string.search(".") );
// => 0
//需要修改成下列形式之一
console.log( string.search("\\.") );
console.log( string.search(/\./) );
// => 4
// => 4
  • regex#execstr#match 比较:在没有修饰符g时,使用 str#match 可以获取完整信息,如果有修饰符g时,也想获得完整信息,用一下 regex#exec
var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
var result;
while ( result = regex2.exec(string) ) {
	console.log( result, regex2.lastIndex );
}
// => ["2017", "2017", index: 0, input: "2017.06.27"] 4
// => ["06", "06", index: 5, input: "2017.06.27"] 7
// => ["27", "27", index: 8, input: "2017.06.27"] 10

结尾

我猜这里的demo们的信息量有一点点爆炸💥 👀,凝聚就是精华!

基础篇包含了:

  • 匹配内容:匹配字符或者位置(量词、字符组、贪婪&惰性匹配、排除字符组 balabalaba)
  • 括号 -> 引用
  • JavaScrip的正则API们

最后扔两个非常有意思的函数,和这群API中某两个相关:

字符串压缩:str#replace

//str#replace做压缩
function compress(source) {
	var keys = {};
	source.replace(/([^=&]+)=([^&]*)/g, function(full, key, value) {
		keys[key] = (keys[key] ? keys[key] + ',' : '') + value;
	});
	var result = [];
	for (var key in keys) {
		result.push(key + '=' + keys[key]);
	}
	return result.join('&');
}

console.log( compress("a=1&b=2&a=3&b=4") );
// => "a=1,3&b=2,4"

类型判断:字符串做信息存储,str#split

//类型判断:使用字符串存储信息
var utils = {};
"Boolean|Number|String|Function|Array|Date|RegExp|Object|Error".split("|").forEach(function(item) {
	utils["is" + item] = function(obj) {
		return {}.toString.call(obj) == "[object " + item + "]";
	};
});
console.log( utils.isArray([1, 2, 3]) );
// => true

参考链接

老姚-JS正则表达式完整教程:感谢老姚🙏

入门教程