正则表达式的 4 个实际用例
GenAI LIVE! | 2025年6月4日
最初发布于此处:https://blog.bitsrc.io/4-practical-use-cases-for-regular-expressions-b6ab140894fd
如何不再惧怕 RegExp 对象并学会喜欢它
正则表达式常常让新开发人员感到畏惧,他们看到奇怪的语法并选择避免添加额外的逻辑来解决他们的需求,而不是试图理解它们背后的逻辑。
别误会,我刚开始的时候自己也这样做过,处理一种语言的语法就足够了,所以为了使用这些正则表达式而必须学习一些奇怪的额外语法的简单想法并不适合我。
让我改变想法并决定尝试学习读写它们的主要原因,是了解了它们的用例。在本文中,我希望为你提供同样的帮助,所以,让我们开始吧。
首先,简单介绍一下 JavaScript 中的正则表达式
我喜欢将正则表达式描述为“类固醇中的字符串”(请随意引用我的话),这是因为与好的字符串对象相比,你可以用它们做更多的事情。
虽然普通字符串可以让你执行诸如连接、长度计算甚至现在的操作,但使用 ES6:模板;正则表达式可以让你找到模式,进行模糊匹配,甚至在我们值得信赖的朋友:字符串之上执行选择性替换。
我知道你在想什么:那可怕的语法怎么办?!我和你的想法完全一致,我已经用它们很多年了,每次我需要做一些除了基本模式匹配之外的事情时,我都需要上网查一下正确的方法。
话虽如此,你还能如何实现它呢?他们实际上向字符串实体添加了太多功能,以至于无法将它们全部纳入对象的 API(更不用说正则表达式也是非面向对象语言的一部分,那么你该怎么办呢?)。
让我分解一下基本语法,以确保我们都在同一页面上,然后你就会看到事情是如何开始变得有意义的。
正则表达式的剖析
最后声明一下,我这里使用的是 JavaScript 风格的正则表达式。如果您尝试将以下示例改编成其他语言,请务必检查语法是否正确,因为可能会有细微的改动。
在 JavaScript 中,正则表达式可以通过以下两种方式之一定义:
- 使用RegExp 对象,它是一个全局对象,您可以在任何地方使用它,而无需添加或要求(我正在看着你 Node.js 开发人员)任何额外的东西。
let regExp = new RegExp('a|b');
- 使用文字表示法,即用一对“/”包围来定义
let regExp = /a|b/;
两个版本返回的内容相同,我个人更喜欢第二个版本,因为它不需要额外的直接实例化。不过,如果你想从字符串创建正则表达式(例如,你可能有一个字符串,你可以根据不同的条件定义实际的表达式),那么第一个版本就非常方便了。所以一定要记住这两个版本。
修饰符或标志
无论您如何称呼它们,它们都会为您的正则表达式增添额外的含义。共有六个正则表达式,其中一些您会经常使用,而其他一些您一生中可能只用一两次,所以让我们快速介绍一下它们:
-
g :执行全局搜索。换句话说,它不会在找到第一个匹配项后返回,而是返回在字符串中找到的所有匹配项。
-
i :不区分大小写的搜索。这个方法非常简单(而且很有用),因为它会在匹配过程中忽略大小写,否则像“Hello”和“HELLO”这样的单词将不会被视为匹配。
-
m :多行搜索。与第一个类似,但如果字符串中有换行符,此标志将忽略它们,并且不会在它们上面停止。
-
s :允许 . 匹配换行符。通常,点字符匹配除换行符之外的任何单个字符。
-
u:“unicode”;将模式视为 unicode 代码点序列。
-
y :执行“粘性”搜索,从目标字符串的当前位置开始匹配。如果您一次只进行一个搜索,这将非常方便,因为它会从上次尝试找到的最后一个位置开始搜索。
这些标志被添加到正则表达式的末尾,如下所示:
//If you're using the RegExp object
let re = new RegExp('[H|h]ello', 'gm');
//If you're going with the literal syntax
let re = /[H|h]ello/gm;
这就是我对正则表达式的自定义介绍,如果您想了解它们的工作原理,请查看文档,但首先,请继续阅读并查看以下实际示例,以便您能够理解文档。
正则表达式用例
以下 4 个用例旨在向您展示正则表达式有多么有用,不仅适用于代码逻辑需求,而且大多数 IDE 实际上都支持使用它们在代码中搜索和替换文本。
密码模式匹配
在您最喜欢的网站上尝试创建帐户时,您是否看到过这样的消息:“您的密码必须至少包含 8 个字符,至少包含一个大写字母、一个小写字母、一个数字和一个符号,以确保您将来永远不会记住它”
好吧,也许最后一部分是我写的,但你明白我的意思:它们描述了你需要遵循的模式,以便提供有效的密码。当然,你可以使用简单的 JavaScript 代码来验证,但如果你能用一行代码描述整个模式,那又何必呢?
您可以使用以下正则表达式:
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W).{8,}$/g
以下是供您测试的简短代码片段:
let re = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W).{8,}$/g
let passwords = ["Fernando", "f3rn4", "F3rnand0!", "fernando123!"]
passwords.forEach( p => {
let matches = p.match(re)
if(!matches) console.log(p, "INVALID PASSWORD")
else console.log(p, "is a valid password!")
})
/*
Fernando INVALID PASSWORD
f3rn4 INVALID PASSWORD
F3rnand0! is a valid password!
fernando123! INVALID PASSWORD
*/
本质上,我们使用了一种叫做“正向前瞻”的东西,它们是表达式的一部分,引擎会在文本中搜索它们,无论它们位于何处。 (?=...) 中的所有内容都是我们关心的表达式部分。
-
(?=.*[az]) 本质上意味着它将匹配后面跟着小写字母的任何字符。
-
(?=.*[AZ]) 与前一个类似,但不是小写,而是如果后面的字符是大写,它就会匹配。
-
(?=.*\d) 将匹配后面跟着数字的任何内容。
-
(?=.*\W) 匹配后面跟着符号的任何字符(换行符除外)。
-
.{8,} 确保匹配的长度至少为8 个字符(由于那里有一个点,所以可以是任意字符)。
-
^ 和 $ 确保匹配从单词开头开始(得益于表达式开头的插入符号),并以单词结尾(得益于美元符号)。本质上,只允许全词匹配。不考虑部分匹配。
如果满足上述所有条件,则返回匹配,否则它将不是有效密码。
电子邮件格式检查器
以前我做 Web 开发的时候,可能已经实现过近百万次了。你在注册表单里看到过多少次“邮箱格式无效”的消息?现在,“email”类型的输入元素已经可以执行这个验证了。
话虽如此,如果您正在进行后端验证或由于某种原因无法访问此字段,则正则表达式可以帮助您在一行代码中验证此格式,而不是使用几个不同的 IF 语句。
以下是完整检查电子邮件地址的神奇正则表达式:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
我知道,这很多,但如果你仔细观察,你可以识别出其中地址预期格式的所有三个部分:
首先,我们检查用户名是否有效,这只是检查是否使用了所有有效字符,并且至少添加了其中一个字符(这就是末尾的“+”的意思):
^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+
然后,我们检查@字符和主机名:
@[a-zA-Z0-9-]+
再次,没有什么特别的,主机名需要是字母数字并且至少有一个字符。
最后,可选部分负责检查 TLD(顶级域名),或者基本上是域名扩展名:
(?:\.[a-zA-Z0-9-]+)*$/
由于末尾有 * ,所以可以看出这部分是可选的。这意味着该组(组由括号分隔)的实例必须为 0 个或多个(因此 .com 可以匹配,但 .co.uk 也可以)。
以下是显示工作中表达式的简短片段:
let emailRE = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
let emails = ["fernando", "fernadno@", "fernando@test", "fernando@test.com", "valid_email123@host2.com", "a@1.com"]
emails.forEach( p => {
let matches = p.match(emailRE)
if(!matches) console.log(p, "INVALID EMAIL")
else console.log(p, "is a valid email!")
})
/*
fernando INVALID EMAIL
fernadno@ INVALID EMAIL
fernando@test is a valid email!
fernando@test.com is a valid email!
valid_email123@host2.com is a valid email!
a@1.com is a valid email!
*/
智能角色替换
模式验证已经足够了,让我们做一些字符串修改,好吗?
这是正则表达式的另一个亮点,它允许你进行一些非常复杂的字符替换。在这个特定示例中,我将向你展示如何将驼峰式命名法(也就是你写出“WriteEverythingLikeThis”的那个)转换为普通命名法。这是一个简单的例子,但足以向你展示如何使用捕获组来实现。
现在,在看代码之前,想一想,如果没有正则表达式,你会怎么做?你可能需要一些大写字母列表,然后对每个字母运行替换程序。可能还有其他方法,但这个是我能想到的最简单的。
这是正则表达式的替代方案:
let camelRE = /([A-Z])/g
let phrase = "thisIsACamelCaseString"
console.log(phrase.replace(camelRE, " $1")
/*
this Is A Camel Case String
*/
没错,就是它!捕获组(括号及其内部的所有内容)保存了匹配的部分,你可以用“$1”引用它。如果你有多个捕获组,则可以递增该数字($2、$3,以此类推)。这里的重点是,表达式只会匹配字符串中任意位置的单个大写字符(这要归功于末尾的 g 标志),并且你可以用以空格为前缀的自身替换它(这要归功于 replace 方法调用)。
现在让我向您展示一个更复杂的字符串替换情况。
从老式函数到箭头函数
这个很有趣,因为您可以为它编写一些代码来取乐,或者在更现实的情况下,您可能会使用 IDE 的搜索和替换功能来执行此操作!
考虑到箭头函数相对较新,仍然有很多遗留代码没有使用它们,您可能倾向于切换,但手动修改每个函数可能需要很长时间,因此,您可以使用正则表达式。
为了把事情说清楚,我想这样说:
function sayHello(first_name, last_name){
console.log("Hello there ", first_name, last_name)
}
变成这样:
const sayHello = (first_name, last_name) => {
console.log("Hello there ", first_name, last_name)
}
因此,本质上,我们需要捕获函数的名称、它的参数列表及其内容,然后重构它,以便删除函数字并创建新的常量。换句话说,我们需要三个捕获组,如下所示:
function (.+)(\(.+\))(\{.+\})
接下来只需调用 replace 方法即可。同样,您可以使用自己喜欢的 IDE 来完成此操作,但这里有一个快速的 Node.js 脚本可以尝试:
const fs = require("fs")
const regExp = /function (.+)(\(.+\))(\{.+\})/gms
fs.readFile("./test2.js", (err, cnt) => {
console.log(cnt.toString().replace(regExp, "const $1 = $2 => $3"))
})
上面的代码将输出我们想要的箭头函数以及任何其他你需要的功能。另外需要考虑的是我使用的标志。因为我们需要确保也捕获换行符,所以我们需要进行多行匹配,并允许点字符也匹配这些换行符。
话虽如此,这就是我想要向您展示的实际用例列表。
结论
希望到目前为止,通过上面的例子,您已经看到了正则表达式所能带来的强大功能,尽管它们看起来并不漂亮,但也不难理解。
因此,如果您还没有这样做,请尝试一下并尝试将这个新工具添加到您的开发工具集中。
如果您对正则表达式并不陌生,请在下面发表评论并告诉我们您如何使用它们!
下次再见!
鏂囩珷鏉ユ簮锛�https://dev.to/deleteman123/4-practical-use-cases-for-regular-expressions-58c2