简介
匹配字符串或字符串模式可能是一个真正的斗争。在最常见的情况下,你需要这些来验证_电子邮件、用户输入、文件名_或大多数类型的输入字符串。虽然有许多模式匹配库和方法--一个经过时间考验的方法是使用 正则表达式来定义一组特定字符串必须遵循的规则,以匹配该模式。
在JavaScript中,RegExp 类用于表示_正则表达式_,并可以与一些方法结合,使匹配模式更容易。
很明显,使用这些方法的前提是要有正则表达式的知识。如果你对编写正则表达式感到不舒服,你总是可以使用RegEx测试网站,如regex101.com 或 regexr.com--它们可以直观地显示你的表达式对给定字符串的影响。
在本指南中,我们将研究JavaScript中的正则表达式,
RegExp()类的用法,以及exec()和test()方法。
之后,我们将看看用String 对象实现的一些方法--match() 、search()和replace() ,它们与正则表达式一起工作,作为使用RegEx 类的一个较短的替代方案。
什么是正则表达式?
在我们深入研究使用RegEx的JavaScript的API之前,让我们先看看正则表达式本身。如果你已经熟悉了它们--这可以作为一个复习,或者你可以完全跳过这个部分。
正则表达式(缩写为_RegEx_)是一种字符模式,用于匹配不同的字符串或字符组合。为了形成一个正确的正则表达式,你需要遵循某些规则。我们将快速浏览这些规则并举例说明:。
[abc]- 匹配_单个字符_:a、b或c[^abc]- 匹配_除_a、b或c_以外_的所有字符[a-z]- 匹配_范围为a-z_的任何字符\s- 匹配任何_空白_字符\w- 匹配任何_单词_字符
这些是一些 基本的模式,但它们可以让你走得更远。正则表达式也支持运算符。
a?- 操作员?匹配_零或一个_字符aa*- 操作员*匹配零个_或多个_字符aa+- 操作员+匹配_一个或多个_字符aa{n}- 操作员{n}与字符a相匹配,_且_连续出现n。a{n, m}- 操作员{n, m}与字符a相匹配,且连续出现在n和m之间。\.- 操作符\转移了字符.,这意味着字符.不会有其通常的含义--匹配任何字符串--而是作为一个字符被匹配。.
为了将其付诸实践--让我们写一个正则表达式,检查一个字符串是否在字符串的末尾包含@gmail.com ,并且在@ 符号之前包含三个字符a 。
"\w+a{3}@gmail\.com"
让我们快速分解一下。
\w- 匹配任何字符a{3}- 匹配连续的三个字符a@gmail\.com- 匹配一个字面字符串_"@gmail.com"_,同时用一个\操作符来转义.
通过这个_RegEx_,我们可以匹配如下字符串。
someEmailaaa@gmail.com
baaa@gmail.com
但不是。
aaaSomeEmail@gmail.com
aaa@gmail.com
aaa@yahoo.com
你可以在一个可视化的RegEx测试器中测试这些,看看哪些部分是匹配的,为什么?
_正则_类
在 JavaScript 中,有两种创建正则表达式的方法。
- 使用_RegEx字面_,这是一个放在
/字符之间的模式。
let regex = "/[abc]+/";
如果你的RegEx在整个脚本中保持不变,你应该使用这种方法,因为这种RegEx在脚本自动加载时就被编译了。
- 使用
RegExp()构造函数。
let regex = new RegExp("[abc]+");
当你的RegEx是动态的,并且在脚本的整个生命周期中都可能发生变化时,这种方法是首选。它在_运行时_被编译,而不是_加载时_。
**注意:**从ES6开始,你也可以把_RegEx的字面意思_作为构造函数的一个参数来传递。
let regex = new RegExp(/[abc]+/);
当使用RegExp ,你也可以传递标志--有意义的字符--来改变模式的匹配方式。其中一些标志是。
i- 表示_不区分大小写_,因此A和a在匹配时是一样的。
// Matches both ABC and abc one or more times
let regex = new RegExp("[abc]+", "i");
-
g- 表示所有可能的情况都将被匹配,而不仅仅是遇到的第一个情况。 -
m- 表示_多行模式_,允许用写在多行中的字符串来匹配该模式。
let string = `
This string can also be matched with
Even though it's written in multiple lines
`
RegExp() 构造函数仅用于创建一个要测试的模式。然而,它包含两个方法,可以测试出该模式,并在适合的情况下与之匹配:exec() 和test() 。
exec() 方法
exec() 方法,没有太多的惊喜,_执行_了一个字符串的搜索。如果有一个匹配,它返回一个包含匹配信息的数组,否则,它返回null 。
为了处理潜在的
null值,你可以使用ECMAScript 2020中添加的Null Coalescing Operator。
让我们在电子邮件的例子上测试一下--我们要检查一封电子邮件是否以@gmail.com 结尾,并且在@gmail 域之前连续包含三个a 字符。
另外,我们将使用_不区分大小写的标志_。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "i");
let result1 = regex.exec("someEmailaaa@gmail.com");
let result2 = regex.exec("sOmEEmAiLaAa@gmail.com");
console.log(result1);
console.log(result2);
或者你可以应用Null Coalescing Operator来保证null-的安全。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "i");
let result1 = regex.exec("someEmailaaa@gmail.com") ?? 'No matched results';
let result2 = regex.exec("sOmEEmAiLaAa@gmail.com") ?? 'No matched results';
让我们看一下输出结果。
[ 'someEmailaaa@gmail.com',
index: 0,
input: 'someEmailaaa@gmail.com',
groups: undefined ]
[ 'sOmEEmAiLaAa@gmail.com',
index: 0,
input: 'sOmEEmAiLaAa@gmail.com',
groups: undefined ]
这个数组包含多种内容。
- 匹配的字符串
- 匹配字符串开始的_索引值_
- 输入的字符串
- groups属性,它持有一个所有命名的捕获组的对象--在大多数情况下,这将是
undefined
如果你希望只分离出匹配的字符串而不需要额外的信息,你可以打印出
console.log(results[0])
exec() 方法的一个有趣的特点是,它记住了它停止_执行_的字符的索引,所以基本上,你可以一次又一次地调用这个方法,直到你得到一个null 的回报。
这个属性被称为lastIndex 。为了实现这一点,你可以向exec() ,传递一个字符串_数组_,而不是一个字符串。
让我们传递一个包含三个字符串的数组;其中两个将被匹配,一个不会。为了得到多个结果,我们可以循环浏览数组并调用exec() ,直到得到一个null 。另外,让我们创建一个空数组matchedStrings ,并将匹配的字符串推送给它。
注意:你可以将 必须将g 标志传递给RegExp() 构造函数,以便获得所有的结果,而不仅仅是第一个结果。这样,你就可以避免进入一个无限循环,而没有人喜欢无限循环。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "g");
let strings = ["someEmailaaa@gmail.com", "baaa@gmail.com", "baaa@yahoo.com"];
let matchedStrings = [];
let result = regex.exec(strings);
if(result != null) {
matchedStrings.push(result[0]);
}
while(result != null) {
result = regex.exec(strings);
if(result != null) {
matchedStrings.push(result[0]);
}
}
console.log(matchedStrings);
这样的结果是。
["someEmailaaa@gmail.com", "baaa@gmail.com"]
你可以看到,我们从未跟踪过数组中最后执行的字符串的索引,但exec() 知道在哪里继续搜索。相当整齐!
test() 方法
test() 方法与exec() 相似,只是它没有返回一个包含信息的数组,而是返回一个简单的true 或false 。它执行与exec() 相同的搜索,如果一个模式与一个字符串相匹配,它返回true 。否则,它返回false 。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "i");
let results = regex.test("someEmailaaa@gmail.com");
console.log(results); // Output: true
results = regex.test("someEmailaaa@yahoo.com");
console.log(results); // Output: false
这个方法不能返回一个null ,你可以用这个结果来支配进一步的条件逻辑。
test() 方法还能记住执行的lastIndex ,所以你可以测试出一个字符串数组。然而,如果你测试同一个字符串两次,你会得到不同的结果。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "g"); // Remember the 'g' flag when working with multiple results
let results = regex.test("someEmailaaa@gmail.com");
console.log(results); // Output: true
results = regex.test("someEmailaaa@gmail.com");
console.log(results); // Output: false
我们第二次得到false ,是因为lastIndex 已经移到了字符串的末尾,所以当它第二次开始搜索时--它从字符串的末尾开始--没有什么可以匹配。因此,它返回false 。
如果你使用test() 来实现预期的行为,你就必须确保没有重复。
使用test() 与一个字符串数组的用法与exec() 相同,只是你将打印出true/false 。在实践中,这并不常用,除非你要跟踪匹配的字符串的数量。
match() 方法
match() 方法是我们要看的String 方法中的第一个,它与_正则表达式_配合得很好。
它接受一个_RegEx_作为参数,并返回一个_匹配的数组_,如果没有,则返回null ,所以本质上--与RegEx 实例的exec() 方法的API基本相同。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "g"); // Note the 'g' flag
let string = "someEmailaaa@gmail.com";
let resultArray = string.match(regex);
console.log(resultArray); // Output: [ 'someEmailaaa@gmail.com' ]
**注意:**你也可以在这里使用_RegEx字面_,以缩短代码,因为无论如何它都会被编译成一个RegEx instance。
let string = "someEmailaaa@gmail.com";
let resultArray = string.match(/\w+a{3}@gmail\.com/);
console.log(resultArray); // Output: [ 'someEmailaaa@gmail.com' ]
为了更好地了解这个方法,让我们把RegEx改为/[a-z]/ - 只匹配小写字符。
let regex = new RegExp(/[a-z]/, "g"); // Note the 'g' flag
let string = "someEmailaaa@gmail.com";
let resultArray = string.match(regex);
console.log(resultArray);
这将产生一个包含字符串中所有小写字符的数组。
["s","o","m","e","m","a","i","l","a","a","a","g","m","a","i","l","c","o","m"]
search() 方法
search() 方法_搜索_传递的模式和字符串之间的匹配。如果找到了一个匹配,就会返回其_索引_。否则,该方法返回-1 。
let regex = new RegExp(/\w+a{3}@gmail\.com/, "g"); // Note the 'g' flag
let string = "some string that isn't matched someEmailaaa@gmail.com";
let result = string.search(regex);
console.log(result); // Output: 31
string = "It should return -1 with this string";
result = string.search(regex);
console.log(result); // Output: -1
当你想知道是否找到了一个匹配项_以及_它的索引时,应该使用这个方法。如果你只想知道是否找到了一个匹配项,你应该使用test() 。
你也可以从exec() 方法中提取这些信息,但这需要你在一个数组中匹配一个元素,而这个方法返回的结果更容易解析。
replace() 方法
replace(to_replace, replace_with) 方法返回一个新的字符串,其中模式匹配to_replace 被替换为replace_with 。
它不会改变原来的字符串,因为字符串是不可变的。
to_replace 参数可以是一个字符串,也可以是一个RegExp 实例。如果是一个字符串,只有第一次出现的部分会被替换,而如果是一个RegExp ,每一个都会被替换。
就本方法而言,让我们用yahoo.com替换 gmail.com 。
let regex = new RegExp(/gmail\.com/, "g"); // Note the 'g' flag
let string = "example@gmail.com";
let result = string.replace(regex, "yahoo.com");
console.log(result); // Output: example@yahoo.com
string = "example1@gmail.com example2@example.com"
result = string.replace(regex, "yahoo.com");
console.log(result); // Output: example1@yahoo.com example2@yahoo.com
console.log(string); // Output: example1@gmail.com example2@gmail.com
正如你在第二个例子中所看到的,所有符合regex 的出现都被替换为yahoo.com 。另外,原始字符串没有变化。
总结
尽管正则表达式可能很难阅读,而且一开始也很难理解,但在理解了正则表达式之后,使用它们和构造它们会变得很有趣。
JavaScript确保使测试和匹配尽可能的简单,你所需要做的就是学习正则表达式。
然而,有了今天的工具和类似于本指南开头所列的网站,你可以很容易地绕过实际学习正则表达式的所有规则。
在本指南中,我们已经涵盖了。
RegExp类 - 一个用来表示正则表达式的对象的类exec()方法 -在一个字符串中搜索一个正则表达式,并返回一个匹配数组(有附加信息)。test()方法 - 只测试一个字符串中是否有匹配项,并返回true/false。match()方法 -定义在String类中,返回一个匹配数组(没有附加信息)。search()方法 -_定义在String类_中,返回一个找到的匹配的索引。replace()方法 -定义在String类中,用一个字符串替换一个RegExp()。
对于正则表达式来说,最好的做法可能是尝试和测试用于_电子邮件和密码验证_的表达式。