在阅读了前面的教程之后,您现在应该对反斜杠等特殊字符和or等字符序列有了很好的理解\W
。以下是这些字符序列的快速摘要:
您可以使用
\d
或\D
分别匹配任何给定字符串中的数字或非数字字符。数字字符包括 0、1、2、3、4、5、6、7、8 和 9。所有其他字符将由 匹配\D
。您可以使用
\w
或\W
匹配任何给定字符串中的单词或非单词字符。单词字符包括字母、数字和下划线。其他所有字符,例如 ₹、% 等,都被视为非单词字符。您可以使用
\s
或\S
匹配字符串中的空格字符或非空格字符。空格字符包括空格、制表符、换页符和换行符。
*
您可以使用符号来匹配前面的表达式零次或多次,而不是一次匹配一个字符。该+
字符将与前面的表达式类似地匹配 1 次或多次。
您可以通过附加来匹配模式任意特定次数{n, m}
。这里, n
是你想匹配它的最小次数,m
是最大限制。如果不为 指定值m
,则前面的表达式将匹配尽可能多的次数。
如果我们刚刚介绍的任何内容不清楚,您应该查看我之前的教程。我在那里更详细地解释了一切。
现在,让我们继续讨论正则表达式中一些更复杂的字符序列,以便您可以充分利用它们并弄清楚如何编写匹配复杂模式的表达式。
?
使用字符的非贪婪匹配
角色在不同的情况下?
意味着不同的东西。
单独使用时,此字符匹配出现在它之前的表达式 0 或 1 次。在这个意义上,它与{0,1}
.
您也可以?
在其他量词之后立即使用*
,+
和{}
来匹配可能的最小字符数。换句话说,它将把那些贪婪的量词变成非贪婪的。如果不查看实际示例,这可能有点难以理解,所以让我们先看一个示例。
考虑以下句子:
我被分配了 17321HDGE 作为用户 ID,而我的朋友被分配了 FHES193EK1。
现在,让我们看看由不同量词返回的所有匹配项及其非贪婪对应项。
如果我们使用/\d+/g
示例中的表达式,它将匹配一个或多个连续的数字字符。由于全局标志,将有三个匹配项:17321、193和1。
您应该注意193和1被认为是不同的匹配项,因为它们由EK分隔。
以下示例显示了不使用任何量词的匹配。
var re = /\d+/g; var count = 0; var textString = "I have been assigned 17321HDGE as user id while my friend was assigned FHES193EK1."; var match = re.exec(textString); while(match !== null) { console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output 17321 193 1 Total Matches: 3 */
现在,?
在后面添加一个字符\d+
将返回九个不同的匹配项。基本上,/\d+?/
会将每个数字字符变成一个单独的匹配项。这是为什么?
这是因为\d+
根据定义应该匹配一个或多个数字。由于该?
字符应该匹配尽可能少的字符数,因此它一次只匹配一个数字。
这次非贪心?
量词将返回 9 个较小的个位数匹配。为简洁起见,我已经注释掉了将匹配记录到控制台的行。
var re = /\d+?/g; var count = 0; var textString = "I have been assigned 17321HDGE as user id while my friend was assigned FHES193EK1."; var match = re.exec(textString); while(match !== null) { // console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output Total Matches: 9 */
让我们再举一个例子。/\w+/
只要不被空格等非单词字符打断,正则表达式就会一直匹配单词字符。在我们的例子中,它将一次匹配一个以空格分隔的单词,例如assigned和17321HDGE。
如果我们用 替换原来的正则表达式/\w+/
,我们将得到 14 个不同的匹配项。基本上,每个单词都有自己的匹配项。您可以通过注释掉该行来自己查看输出。
var re = /\w+/g; var count = 0; var textString = "I have been assigned 17321HDGE as user id while my friend was assigned FHES193EK1."; var match = re.exec(textString); while(match !== null) { // console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output Total Matches: 14 */
现在,将表达式更改为/\w+?/
将返回每个单词字符作为单独的匹配项,您将获得 68 个匹配项。
在我们继续之前,让我们看一下最后一个示例。正则表达式/\w{4,}/
将返回我们句子中所有四个字符或更长的单词。所以它匹配have、been、assigned和17321HDGE等。现在将其转换为/\w{4,}?/
将从超过四个字符的单词中返回多个匹配项。在我们的示例中,返回的匹配项将是have、be、assi、gned、1732和1HGD。17321HDGE末尾的字符E不属于任何匹配项 因为它不可能在任何四个连续单词字符的组中。
var re = /\w{4,}/g; var count = 0; var textString = "I have been assigned 17321HDGE as user id while my friend was assigned FHES193EK1."; var match = re.exec(textString); while(match !== null) { console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output have been assigned 17321HDGE user while friend assigned FHES193EK1 Total Matches: 9 */
对 ? 使用括号 特点
在我之前的正则表达式教程中,我简要介绍了如何使用括号来记住匹配的一部分。当与?
角色一起使用时,它们也可以用于其他目的。
有时,您希望将一组字符作为一个单元进行匹配。例如,您可能会在以下文本中查找na的出现一次或两次作为匹配项。
na naa nnaa nana naana
为澄清起见,您正在寻找粗体文本作为匹配项:na na a n na a (nana) na a na。括号中的部分应该作为一个单元匹配,所以它只算一个匹配。
几乎所有刚开始使用正则表达式的人都会使用该表达式/na{1,2}/
来获得预期的结果。在他们看来,{1,2}部分应该与n和a 的一两次匹配。但是,它实际上匹配一次出现的n后跟出现 1 次或 2 次出现的字符a。
/na{1,2}/
为了澄清,我已经用粗体呈现了返回的匹配项: na naa n naa (na)(na) (naa)(na)。括号中的部分是单独的匹配项。如您所见,我们没有得到我们想要的结果,因为{1,2}
没有将na视为必须匹配的单个单元。
这里的解决方案是使用括号告诉javascript匹配na作为一个单位。然而,正如我们在之前的教程中看到的,JavaScript 会因为括号而开始记住匹配项。
如果您不希望 javaScript 记住匹配项,则必须?:
在您尝试匹配的字符组之前添加。在我们的例子中,最终表达式将变为/(?:na){1,2}/
。组na现在将作为一个单元匹配,并且不会被记住。我以粗体突出显示了使用此表达式返回的最终匹配项:na na a n na a ( nana ) na a na。
以下示例将所有匹配记录到控制台。由于总共有 6 个匹配项,因此总匹配项数为 6。
var re = /(?:na){1,2}/g; var count = 0; var textString = "na naa nnaa nana naana"; var match = re.exec(textString); while(match !== null) { console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output na na na nana na na Total Matches: 6 */
前瞻和否定前瞻
在许多情况下,我们希望匹配给定的一组字符,但前提是它们后面跟着或不跟另一组字符。例如,您可能正在文本中查找单词apples,但只想要那些后跟are的匹配项。考虑下面的句子。
苹果很好吃。我们一整天都在吃苹果。吃过苹果的人都喜欢。
在上面的示例中,我们只希望第一个单词作为匹配项。该单词的所有其他出现不应出现在匹配项中。
实现此目的的一种方法是使用以下正则表达式a(?=b)
。我们要匹配的词是a,应该在 a 之后的词是b。在我们的例子中,表达式将变为/apples(?=\sare)/
。请记住,单词are不包含在此匹配项中。
var re = /apples(?=\sare)/g; var count = 0; var textString = "apples are yummy. We ate apples all day. Everyone who ate apples liked them."; var match = re.exec(textString); while(match !== null) { console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output apples Total Matches: 1 */
这个正则表达式,我们在确定单词是否匹配之前查看字符串中接下来的内容,称为前瞻。
如果您决定仅在没有特定字符集的情况下匹配苹果,则会出现非常相似的情况。在这种情况下,您需要在正则表达式中替换为。如果我们要查找所有没有紧跟are的苹果,我们将使用它作为正则表达式。我们的测试句子将有两个成功的匹配。?=
?!
/apples(?!\sare)/
var re = /apples(?!\sare)/g; var count = 0; var textString = "apples are yummy. We ate apples all day. Everyone who ate apples liked them."; var match = re.exec(textString); while(match !== null) { console.log(match[0]); match = re.exec(textString); count++; } console.log("Total Matches: " + count); /* Output apples apples Total Matches: 2 */
还有一件事——您不需要使用两个单独的正则表达式来查找所有后跟两个给定单词中的任何一个的匹配项。您所要做的就是在这些单词之间添加管道运算符,您就可以开始了。例如,如果您要查找后跟are或were的所有 apple ,则应使用/apples(?!\sare|\swere)/
正则表达式。
最后的想法
在本教程中,我们学习了如何编写复杂的正则表达式来匹配我们正在寻找的模式。我们可以使用特殊?
字符返回前面字符的最小所需数量作为匹配项。同样,我们可以使用?
内括号来确保我们匹配的组不被记住。
最后,我们了解到,正则表达式中的?=
和?!
字符序列使我们有机会返回一组特定的字符作为匹配项,前提是它们后面跟着或不跟着另一组给定的字符。