原文: JavaScript Regex Match Example – How to Use JS Replace on a String

正则表达式 Regular expressions,缩写为 regex,或有时为 regexp,是你可能知道的真正强大和有用的概念之一。但它们可能令人生畏,特别是对初学的程序员来说,更是如此。

其实不一定是这样的。JavaScript 包括几个有用的方法,使正则表达式的使用更容易。在这些方法中,.match().matchAll().replace() 方法可能是你最经常使用的。

在本教程中,我们将介绍这些方法,并看看为什么你会使用这些方法而不是其他的 JS 方法。

对正则表达式的快速介绍

根据 MDN,正则表达式是“用于匹配字符串中字符组合的模式”。

这些模式有时可以包括特殊字符(*+),断言(\W^),组和范围((abc)[123]),以及其他使正则如此强大但难以掌握的东西。

就其核心而言,正则就是在字符串中寻找模式——从测试一个字符串的单个字符到验证一个电话号码是否有效,都可以用正则表达式完成。

如果你对正则表达式是个新手,想在继续阅读之前做一些练习,可以看看我们的交互式编码挑战

如何使用 .match() 方法

因此,如果正则是关于在字符串中寻找模式,你可能会问自己是什么使 .match() 方法如此有用?

不像 .test() 方法只是返回 true 或者 false.match() 将实际返回与你测试的字符串的匹配,例如:

const csLewisQuote = 'We are what we believe we are.';
const regex1 = /are/;
const regex2 = /eat/;

csLewisQuote.match(regex1); // ["are", index: 3, input: "We are what we believe we are.", groups: undefined]

csLewisQuote.match(regex2); // null

这对一些项目来说真的很有帮助,特别是当你想在不改变原始字符串的情况下提取和操作你所匹配的数据时。

如果你只想知道一个搜索模式是否被找到,请使用 .test() 方法——它要快得多。

.match() 方法中你可以期待两个主要的返回值:

  • 如果有一个匹配,.match() 方法将返回一个包含匹配内容的数组。我们稍后会详细讨论这个问题。
  • 如果没有匹配,.match() 方法将返回 null

你可能已经注意到了这一点,但如果你看上面的例子,.match() 只匹配 “are” 这个词的第一个实例。

很多时候,你会想知道一个模式与你所测试的字符串的匹配频率,所以让我们看看如何用 .match() 来实现。

不同的匹配模式

如果有一个匹配,.match() 返回的数组有两种不同的模式,因为缺乏更好的术语。

第一种模式是不使用全局标志(g)时,例如在上面的例子中:

const csLewisQuote = 'We are what we believe we are.';
const regex = /are/;

csLewisQuote.match(regex); // ["are", index: 3, input: "We are what we believe we are.", groups: undefined]

在这种情况下,我们 .match() 一个数组,和包含第一个匹配项以及原始字符串中匹配项的索引、原始字符串本身,以及任何使用过的匹配组。

但是,假设你想看看 “are” 这个词在一个字符串中出现了多少次。要做到这一点,只需在你的正则表达式中添加全局搜索标志:

const csLewisQuote = 'We are what we believe we are.';
const regex = /are/g;

csLewisQuote.match(regex); // ["are", "are"]

你不会得到非全局模式所包含的其他信息,但你会得到一个包含你正在测试的字符串中所有匹配的数组。

大小写敏感

要记住的一件重要事情是,regex 是区分大小写的。例如,假设你想看看 “we” 这个词在你的字符串中出现了多少次:

const csLewisQuote = 'We are what we believe we are.';
const regex = /we/g;

csLewisQuote.match(regex); // ["we", "we"]

在这种情况下,你要匹配的是一个小写的 “w” 和一个小写的 “e”,而后者只出现两次。

如果你想要 “we” 这个词的所有实例,无论它是大写还是小写,你有几个选择。

首先,你可以在用 .match() 方法测试之前对字符串使用 .toLowercase() 方法:

const csLewisQuote = 'We are what we believe we are.'.toLowerCase();
const regex = /we/g;

csLewisQuote.match(regex); // ["we", "we", "we"]

或者如果你想保留原来的大小写,你可以在你的正则表达式中添加不区分大小写的搜索标志(i):

const csLewisQuote = 'We are what we believe we are.';
const regex = /we/gi;

csLewisQuote.match(regex); // ["We", "we", "we"]

新的 .matchAll() 方法

现在你已经知道了关于 .match() 方法的所有情况,值得指出的是,最近引入了 .matchAll() 方法。

.match() 方法不同的是,.matchAll() 需要全局搜索标志(g),并返回一个迭代器或一个空数组:

const csLewisQuote = 'We are what we believe we are.';
const regex1 = /we/gi;
const regex2 = /eat/gi;

[...csLewisQuote.matchAll(regex1)]; 
// [
//   ["We", index: 0, input: "We are what we believe we are.", groups: undefined],
//   ["we", index: 12, input: "We are what we believe we are.", groups: undefined]
//   ["we", index: 23, input: "We are what we believe we are.", groups: undefined]
// ]

[...csLewisQuote.matchAll(regex2)]; // []

虽然它看起来只是一个更复杂的 .match() 方法,但 .matchAll() 的主要优势在于它能更好地处理捕获组。

下面是一个简单的例子:

const csLewisRepeat = "We We are are";
const repeatRegex = /(\w+)\s\1/g;

csLewisRepeat.match(repeatRegex); // ["We We", "are are"]
.match()
const csLewisRepeat = "We We are are";
const repeatRegex = /(\w+)\s\1/g;

[...repeatStr.matchAll(repeatRegex)];

// [
//   ["We We", "We", index: 0, input: "We We are are", groups: undefined],
//   ["are are", "are", index: 6, input: "We We are are", groups: undefined],
// ]
.matchAll()

虽然这只是勉强触及表面,但请记住,如果你使用 g 标志并希望得到 .match() 为单个匹配提供的所有额外信息(索引、原始字符串等),使用 .matchAll() 可能更好。

如何使用 .replace() 方法

现在你知道了如何在字符串中匹配模式,你可能想对这些匹配做一些有用的事情。

一旦你找到一个匹配的模式,你最常做的事情之一就是用其他东西替换该模式。例如,你可能想把 “paidCodeCamp” 中的 “paid” 替换成 “free”。Regex 将是一个很好的方法来实现这一点。

由于 .match().matchAll() 返回每个匹配模式的索引信息,取决于你如何使用它,你可以用它来做一些花哨的字符串操作。但是有一个更简单的方法——通过使用 .replace() 方法。

使用 .replace(),你所需要做的就是把你想匹配的字符串或正则表达式作为第一个参数传给它,把要替换的字符串作为第二个参数:

const campString = 'paidCodeCamp';
const fCCString1 = campString.replace('paid', 'free');
const fCCString2 = campString.replace(/paid/, 'free');

console.log(campString); // "paidCodeCamp"
console.log(fCCString1); // "freeCodeCamp"
console.log(fCCString2); // "freeCodeCamp"

最重要的是,.replace() 返回一个新的字符串,而原来的则保持不变。

.match() 方法类似,.replace() 只会替换它找到的第一个匹配模式,除非你使用带有 g 标志的 regex:

const campString = 'paidCodeCamp is awesome. You should check out paidCodeCamp.';
const fCCString1 = campString.replace('paid', 'free');
const fCCString2 = campString.replace(/paid/g, 'free');

console.log(fCCString1); // "freeCodeCamp is awesome. You should check out paidCodeCamp."
console.log(fCCString2); // "freeCodeCamp is awesome. You should check out freeCodeCamp."

与之前类似,无论你传递的是字符串还是正则表达式作为第一个参数,都要记住匹配模式是区分大小写的:

const campString = 'PaidCodeCamp is awesome. You should check out PaidCodeCamp.';
const fCCString1 = campString.replace('Paid', 'free');
const fCCString2 = campString.replace(/paid/gi, 'free');

console.log(fCCString1); // "freeCodeCamp is awesome. You should check out PaidCodeCamp."
console.log(fCCString2); // "freeCodeCamp is awesome. You should check out freeCodeCamp."

如何使用 .replaceAll() 方法

就像 .match() 有一个更新的 .matchAll() 方法一样,.replace() 也有一个更新的 .replaceAll() 方法。

.replace().replaceAll() 之间唯一真正的区别是,如果你用 .replaceAll() 使用正则表达式,你需要使用全局搜索标志:

const campString = 'paidCodeCamp is awesome. You should check out paidCodeCamp.';
const fCCString1 = campString.replaceAll('paid', 'free');
const fCCString2 = campString.replaceAll(/paid/g, 'free');

console.log(fCCString1); // "freeCodeCamp is awesome. You should check out freeCodeCamp."
console.log(fCCString2); // "freeCodeCamp is awesome. You should check out freeCodeCamp."

.replaceAll() 的真正好处是它的可读性更强一些,当你把一个字符串作为第一个参数传给它时,它将替换所有匹配的模式。

现在你知道了用 regex 和一些内置的 JS 方法匹配和替换部分字符串的基本知识。这些都是非常简单的例子,但我希望它仍然显示了即使是一点点的 regex 也可以是多么强大。

这对你有帮助吗?你是如何使用 .match().matchAll().replace().replaceAll() 方法的?请在 Twitter 上告诉我。