# 正则基础
# 元字符和表达式
表达式 | 说明 |
---|---|
\d | 匹配任意一个数字 |
\w | 匹配任意一个字母、数字或下划线 |
\s | 匹配任意一个空白字符(空格、制表符、换行符等) |
. | 匹配除换行符以外的任意一个字符 |
[abc] | 匹配字符集合中的任意一个字符(a、b或c) |
[^abc] | 匹配除字符集合中的任意一个字符以外的字符 |
\b | 匹配单词的边界 |
# 量词
表达式 | 说明 |
---|---|
* | 匹配前面的元素零次或多次 |
+ | 匹配前面的元素一次或多次 |
? | 匹配前面的元素零次或一次 |
{n} | 匹配前面的元素恰好 n 次 |
{n,} | 匹配前面的元素至少 n 次 |
{n,m} | 匹配前面的元素至少 n 次,但不超过 m 次 |
# 执行方法
- test():测试字符串是否匹配模式,返回布尔值。
- exec():在字符串中执行匹配,返回匹配结果的详细信息。
- match():在字符串中查找匹配,返回匹配结果的数组。
- search():在字符串中搜索匹配,返回匹配的位置。
- replace():替换字符串中的匹配项。
- split():根据匹配项将字符串拆分为数组。
# 分组
通过使用括号将需要分组的内容括起来,我们可以创建一个分组,可以用于匹配、替换和提取信息
# 1.基本分组语法
使用小括号 () 来创建一个分组。分组可以包含一个或多个字符,用于表示一个特定的模式。例如,(abc) 表示一个由字符 "abc" 组成的模式。
const str = "I love cats and dogs";
const pattern = /(love cats)/;
const result = str.match(pattern);
console.log(result[0]); // 输出:"love cats"
console.log(result[1]); // 输出:"love cats"
# 2.捕获分组
它允许我们在匹配过程中获取分组的值,并进行后续的处理。捕获组可以通过索引或命名来引用。
# 示例一:在一段字符串中取出其中的电子邮件地址
const str = "My email address is john@example.com";
const pattern = /(\w+@\w+\.\w+)/;
const result = str.match(pattern);
console.log(result[0]); // 输出:"john@example.com"
console.log(result[1]); // 输出:"john@example.com"
使用 (\w+@\w+.\w+) 创建了一个分组,它匹配了电子邮件地址,并将其作为捕获组返回。
# 示例二:在一段字符串中匹配并提取出其中的电话号码
const str = "My phone number is (123) 456-7890";
const pattern = /\((\d{3})\)\s(\d{3})-(\d{4})/;
const result = str.match(pattern);
console.log(result[0]); // 输出:"(123) 456-7890"
console.log(result[1]); // 输出:"123"
console.log(result[2]); // 输出:"456"
console.log(result[3]); // 输出:"7890"
在上述示例中,使用 ((\d{3}))\s(\d{3})-(\d{4}) 创建了三个分组,分别匹配了电话号码的区号、前三位和后四位,并将它们作为捕获组返回。
# 示例三:搭配replace使用
const str = "aaa,bbb,ccc";
str.replace(/(\w+),(\w+),(\w+)/, "$3,$2,$1");//输出'ccc,bbb,aaa'
- $1代表第一个(\w+)匹配到的内容,即aaa
- $2代表第一个(\w+)匹配到的内容,即bbb
- $3代表第一个(\w+)匹配到的内容,即ccc
# 3.非捕获分组
非捕获分组的语法是(?:pattern), 不影响正常匹配行为但是不作为结果返回。
# 示例一:
const str = "My phone number is (123) 456-7890";
const pattern = /(phone)/
const pattern2 = /(?:phone)/
const result = str.match(pattern);
const result2 = str.match(pattern2);
console.log(result[0], result2[0]) // phone , phone
console.log(result[1], result2[1]) // phone , undefined
# 示例二:匹配域名
const str = "https://www.doorverse.com?v=123";
const regex = /(?:http|https):\/\/(?:[a-z]+\.)+(?:[a-z]+)/i;
const result = str.match(regex);
console.log(result[0]); // 输出:"https://www.doorverse.com"
在这个例子中,正则表达式尝试匹配以 "http://" 或 "https://" 开头的 URL,但并不需要捕获这些前缀。因此,这些前缀被放在了一个非捕获分组中。
# 4.反向引用分组
反向引用使用 \数字 的形式,其中数字表示先前捕获的分组的索引。当使用反向引用时,正则表达式引擎会尝试匹配与先前捕获的内容相同的内容。
# 示例一:匹配连续重复的字符
const str = "abab";
const pattern = /(\w+)\1/;
const result = str.match(pattern);
console.log(result[0]); // 输出:"abab"
console.log(result[1]); // 输出:"ab"
在上述示例中,使用 (\w)\1 创建了一个分组,并使用 \1 后向引用来引用先前捕获的字符,从而匹配连续重复的字符。
# 示例二:匹配类型为abab的数字
var str = 'ooo1212ooo2323ooo3434ooo1234';
var reg = /(\d)(\d)\1\2/g;
var result = str.match(reg);
console.log(result); // ['1212', '2323', '3434']
在上述示例中,所定义的reg是, \d匹配数字,\1对应的是第一个表达式(\d),\2对应的是第二个表达式(\d),g是全局匹配
# 前瞻
前瞻是指在匹配过程中,我们可以在当前位置向前查看,以确定是否满足某种条件
# 正向前瞻
语法 ?=
它会在当前位置向前查找
# 示例一:匹配邮箱地址中的用户名
const emails = ['john@example.com', 'jane.doe@example.com', 'foo@bar.com'];
const usernameRegex = /\w+(?=@)/;
emails.forEach(email => {
const match = email.match(usernameRegex);
console.log(match[0]);
});
// john
// jane.doe
// foo
在这个例子中,我们使用了正向前瞻(?=@)来匹配@符号之前的内容。\w+表示匹配一个或多个字母、数字或下划线,(?=@)表示要求在当前位置向前查找,必须紧跟着@符号。
# 示例二:匹配包含特定单词的句子
const text = 'I love JavaScript. JavaScript is amazing. Python is also great.';
const word = 'JavaScript';
const sentenceRegex = /[^.?!]*(?=\bJavaScript\b)[^.?!]*[.?!]/g;
const matches = text.match(sentenceRegex);
console.log(matches);
运行上述代码,我们可以得到以下输出:
[ 'I love JavaScript.', ' JavaScript is amazing.' ]
在这个例子中,我们使用了正向前瞻(?=\bJavaScript\b)来匹配包含特定单词JavaScript的句子。[^.?!]表示匹配任意数量的非句子结束符(句号、问号、感叹号)的字符,\bJavaScript\b表示匹配单词JavaScript,[^.?!][.?!]表示匹配任意数量的非句子结束符后跟一个句子结束符的字符。
# 示例三:匹配密码强度
假设我们要验证用户输入的密码强度,要求密码必须包含至少8位至少一个大写字母、一个小写字母和一个数字。
const passwords = ['password123', 'Pass123', 'passWORD', '123456'];
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
passwords.forEach(password => {
const isValid = passwordRegex.test(password);
console.log(`${password}: ${isValid}`);
});
运行上述代码,我们可以得到以下输出:
password123: false
Pass123: false
passWORD: false
123456: false
在这个例子中,我们使用了正向前瞻(?=.[a-z])、(?=.[A-Z])和(?=.*\d)来分别匹配至少一个小写字母、一个大写字母和一个数字。.{8,}表示匹配至少8个字符。^和$分别表示匹配字符串的开头和结尾,.
表示任何一个字符(不包括换行),*
表示前一个字符可以出现零次或多次。
# 负向前瞻
语法 ?!
它会在当前位置向前查找, 例如,q(?!u)
匹配含有后面不是 'u' 的 'q' 的字符串
# 示例一:匹配不以 "abc" 开头的字符串
const regex = /^(?!abc).*/;
console.log(regex.test("def")); // true
console.log(regex.test("abcxyz")); // false
# 示例二:匹配不以特定字符结尾的字符串
const regex = /^(?!.*[x|y|z]$).*/;
console.log(regex.test("abc")); // true
console.log(regex.test("defx")); // false
# 示例三:匹配不包含特定字符的字符串
const regex = /^(?!.*[x|y|z]).*/;
console.log(regex.test("abc123")); // true
console.log(regex.test("abcx")); // false
# 后顾
后顾是指在匹配过程中,我们可以在当前位置向后查看,用来捕获后半段
# 正向后顾
正向后顾语法结构是 ?<=
,表示匹配位置前面的内容必须满足指定的模式。
# 示例一:匹配 "1" 后面的所有 "a",
const str = "1a2a3a4a";
const regex = /(?<=1)a/
const matches = str.match(regex);
console.log(matches); // 输出: ['a']
这个正则表达式将只匹配第一个 "a",因为它紧跟在 "1" 的后面。
需要注意的是,正则表达式的查找模式在正向后顾中不会被包括在匹配结果中。也就是说,即使 "1" 是我们用来查找 "a" 的模式,"1" 不会被包括在匹配结果中。只有 "a" 会被返回作为匹配结果。
# 示例二:匹配以特定前缀开头的URL
const str = "Visit our website at https://example.com to learn more.";
const regex = /(?<=https:\/\/)\w+\.\w+/g;
const matches = str.match(regex);
console.log(matches); // 匹配了以 `https://` 开头的URL 输出: ["example.com"]
# 示例三:匹配某个位置之前的特定字符
const str = "I love coding, but I hate debugging.";
const regex = /(?<=love\s)\w+/g;
const matches = str.match(regex);
console.log(matches); //匹配了位于 `love` 后面的单词 输出: ["coding"]
# 负向后顾
负向后顾语法结构是?<!
, 它会在当前位置向后查找, 例如,(?<!u)q
匹配不是 'u' 开头的 'q'
# 匹配不是以123开头的abc
const regex = /(?<!123)abc/g;;
console.log(regex.test("abc123")); // true
console.log(regex.test("123abc")); // false