当前位置:嗨网首页>书籍在线阅读

14-分组

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

17.12 分组

到目前为止,所构造的正则表达式能够识别单个字符(重复允许多次匹配,但这依旧是一个单字符匹配)。而分组则允许构造子表达式,它可以被当做一个独立单元来使用。

除了可以创建子表达式,分组还可以帮助“捕获”分组结果,以便后续使用。“捕获”结果是默认功能,不过也有办法创建“非捕获组“,这也是接下来要学习的内容。如果读者已经有一些使用正则表达式的经验,那这个内容可能并不陌生,不过还是鼓励大家把非捕获组当成默认情况来使用,因为它们有一些性能优势,同时,如果不需要分组的结果,也应该使用非捕获组。分组是使用圆括号来指定的,非捕获组看起来像这样(?:),其中,是需要匹配的内容。下面看一些例子,假设现在要匹配后缀为.com、.org和.edu的域名:

const text = "Visit oreilly.com today!";
const match = text.match(/[a-z]+(?:\.com|\.org|\.edu)/i);

分组的另一个好处是可以在分组时使用重复。一般情况下,重复仅被用在重复元字符前面的单个字符上。分组则允许将其用在一整个字符串上。有一个常见的例子是,如果想匹配URL,以及那些以http://、https://或//(独立于协议的URL)开始的URL,可以在分组上使用代表匹配0个或1个(?)的重复元字符

const html = '<link rel="stylesheet" href="http://insecure.com/stuff. css">\n' +
   '<link rel="stylesheet" href="https://secure.com/securestuff.css">\n' +
   '<link rel="stylesheet" href="//anything.com/flexible.css">';
const matches = html.match(/(?:https?)?\/\/[a-z][a-z0-9-]+[a-z0-9]+/ig);

看起来是不是像是一堆乱码?作者也有同感。但这个例子有着很强的代表性,它值得放慢脚步并认真思考。以一个非捕获组开始 (?:https?)? ,注意这里有两个匹配0个或1个的重复元字符。第一个表示“s是可选的“。记住,一般情况下,重复元字符只作用于它左边最近的字符。第二个指向它左边的整个组。所以,整体来看,它会匹配空字符串(没有 https )、 http 或者 https 。继续执行,匹配了两个斜杠(注意,必须对斜杠进行转义:\/\/)。然后,得到了一个相当复杂的字符类。显然,域名可以包含字母和数字,但它们同样可以包含斜杠(只是它们必须以字母开始,且不能以破折号结尾)。

这个例子并不完美。例如,就像匹配//valid.com一样,它会匹配 //gotcha(非顶级域名(TLD))。但退一步说,匹配完整、合法的URL是一件非常复杂的任务,没有必要在这个例子中实现。

如果觉得这些说明很烦人(比如,“它会匹配非法的URL”),那么只要记住一点,使用正则时并不需要一次做完所有的事情。事实上,每当浏览网站的时候,作者都会使用一个与上例非常相似的正则表达式。第一步,先找出所有的URL或者疑似URL的东西,然后做二次分析,筛选出那些非法的,以及不完整的URL等等。不要太纠结于构造一个完美的正则表达式来覆盖所有可能的情况。这样不仅不现实,而且经常会耗费没必要的精力。很显然,有时候也需要考虑所有可能的情况,例如,为了防止注入攻击而检查用户输入。在这种情况下需要额外注意,一定要让正则表达式滴水不漏。