正则表达式起源于对形式语言(formal language)的数学研究,它趋向于极致而简洁的书写风格,所有的字符挤在一起,不仅难以阅读,而且在维护时也非常困难。Javascript正则表达式借鉴自Perl,它不支持注释和空白,且处在不同位置的相同字符都可能代表不同的意义。 但是我们为何又要使用它呢? 这主要是因为,
相比等效的字符串处理,Javascript正则表达式有着明显的性能优势。
参考:https://en.wikipedia.org/wiki/Formal_language
下面通过例子来感受一下:
/**
* 处理一个url串,并捕获其中的协议名、主机名(*)、端口号、路径名、查询条件和哈希值
* 其中,除了主机名是捕获型分组外,其他的皆为非捕获型
*/
// 以简书为例
var url = 'http://www.jianshu.com:8080/u/101db3165437?user=JSoon#info';
// 处理后的url
var parsed = /^(?:([a-zA-Z]+):)?(\/{0,2})([a-zA-Z0-9\.\-]*)(?::([0-9]+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:\#(.*))?$/.exec(url);
// 在控制台打印
console.log(parsed);
结果如下图:
首先来看parsed
,regexp.exec(string)
返回一个结果数组,数组的第一项为该正则表达式的匹配结果,不计入分组。
参考:RegExp.prototype.exec()
从parsed[1]
开始直到parsed[7]
,则是正则表达式分组所匹配到的值。
正则表达式为两个正斜杠之间所夹的符号串/regexp/
组成,在本例中,第一个/
后的^
表示以第一个分组所匹配的字符串开始,它像一个锚,只会匹配以次开头的字符串,否则返回null
。最后一个/
前的$
表示这个字符串的结束,这就能确保匹配的精确性。
分组一:
// 协议名
(?:([a-zA-Z]+):)? // 匹配一段后边紧跟一个冒号的字符串。如果没有,则会返回undefined,因为+告诉了正则匹配器,必须得到至少一个英文字符
// 匹配结果
console.log(parsed[1]); // "http"
(?:)
表示这是一个非捕获型分组,后面的?
表明这是一个非贪婪性匹配?
等同于{0,1}
。表示如果整个字符串中都没有匹配到这个字符串,也不会影响匹配的进行。若去掉这个?
,而字符串中又没有如http:
之类的子串,那么正则匹配将立即结束,并返回null
。
其内部有一个捕获型分组([a-zA-Z]+)
,[a-zA-Z]
是一个字符集,它指定了一组字符串,这里表示所有的大小写英文字母,后边的+
表示贪婪性匹配,等同于{1,}
,即至少匹配一个英文字母。([a-zA-Z]+)
后边的:
表示这段英文字母后紧接着是一个冒号,如若不是,匹配也会失败。
分组二:
// 斜杠
(\/{0,2}) // 匹配0到2个连续的斜杠。如果没有斜杠,则会返回""空串
// 匹配结果
console.log(parsed[2]); // "//"
这个很好理解,在正则中,若想将斜杠之类的特殊字符按照字面量来匹配,则必须使用反斜杠前缀对其进行转义。这里一个小窍门是,如果你拿不准哪些特殊字符需要转义,一个保险的做法是,给任何特殊字符都添加一个反斜杠前缀来使其字面量化。注意反斜杠前缀并不能使字母或数字字面量化。
分组三:
// 主机名
([a-zA-Z0-9\.\-]*) // 匹配至少0次包含大小写英文字母,数字,点号和连字符
// 匹配结果
console.log(parsed[3]); // "www.jianshu.com"
.
和-
被转义,将.
转义是一种保险的做法,因为这里我并不确定它在字符集中是否代表其他含义;而将-
转义的目的是因为连字符在字符集中表示范围,如[0-9]
表示[0123456789]
。
分组四:
// 端口号
(?::([0-9]+))? // 匹配以冒号开始,以及之后的数字。如果没有,其中的捕获分组会返回undefined,因为+告诉了正则匹配器,必须得到至少一个数字
// 匹配结果
console.log(parsed[4]); // "8080"
分组五:
// 路径名
(?:\/([^?#]*))? // 匹配以斜杠开始,以及除?和#之外所有的字符
// 匹配结果
console.log(parsed[5]); // "u/101db3165437"
分组六:
// 查询条件
(?:\?([^#]*))? // 匹配以?开始,以及除#之外所有的字符
// 匹配结果
console.log(parsed[6]); // "user=JSoon"
分组七:
// 哈希值
(?:\#(.*))? // 匹配以#开始,除行结束符以外的所有字符
// 匹配结果
console.log(parsed[7]); // "info"
特别的,一个未被转移的.
会匹配除行结束符\n
和\r
以外的任何字符。
若还需要对匹配结果进行再匹配,则可以对结果再次进行正则处理,这里就不细说了。
当然,除了用正则表达式字面量来创建正则表达式外,还可以使用
RegExp
构造函数来实现。RegExp
适用于需要动态生成正则表达式,而不是事先就明确正则条件的情况时使用。
参考:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp
可以处理正则表达式的方法:
regexp.exec // 返回匹配结果的数组
regexp.test // 返回true or false
string.match // 返回与regexp.exec相同
string.replace // 用正则对匹配结果进行替换,返回替换后的新字符串
string.search // 返回匹配到的字符串的第一个字符的位置,若匹配失败,则返回-1
string.split // 用正则匹配到的字符作为分割器,然后返回分割后的各个字符的数组
欢迎交流,完。