I apologize for the previous error. It seems I do not have the write_file tool available. I will provide the article content directly as a markdown block.
“`markdown
掌握 JavaScript 正则表达式:从入门到精通
正则表达式(Regular Expression,简称 RegEx 或 RegExp)是用于匹配字符串中字符组合的强大模式。在 JavaScript 中,正则表达式是处理文本数据不可或缺的工具,无论是进行表单验证、数据提取、文本替换还是复杂搜索,它都能提供高效且灵活的解决方案。
本文将带领你从正则表达式的基础知识入手,逐步深入到高级用法,助你彻底掌握 JavaScript 中的正则表达式。
第一部分:入门篇 – 正则表达式基础
什么是正则表达式?
正则表达式是由一系列特殊字符组成的模式,用于在文本中查找、匹配或操作字符串。在 JavaScript 中,正则表达式是 RegExp 对象的实例,可以通过两种方式创建:
-
字面量方式 (推荐):
javascript
const regex = /pattern/flags;pattern:匹配模式的字符串。flags:可选,用于指定匹配行为的标志(如g、i、m等)。
-
构造函数方式:
javascript
const regex = new RegExp('pattern', 'flags');
当正则表达式的模式是动态生成时,通常使用构造函数。
基本匹配
最简单的正则表达式就是匹配字面量字符。
javascript
const str = "Hello JavaScript, hello regex!";
const regex = /hello/; // 匹配 "hello"
console.log(regex.test(str)); // true (默认区分大小写)
常用元字符
元字符是正则表达式中具有特殊含义的字符,它们不匹配自身,而是表示某种模式。
| 元字符 | 描述 | 示例 |
|---|---|---|
. |
匹配除换行符外的任何单个字符 | /a.b/ 匹配 axb, a#b |
\d |
匹配任何数字 (0-9) | /\d/ 匹配 1, 5 |
\D |
匹配任何非数字字符 | /\D/ 匹配 a, # |
\w |
匹配任何字母、数字或下划线 ([a-zA-Z0-9_]) | /\w/ 匹配 a, 1, _ |
\W |
匹配任何非字母、数字或下划线字符 | /\W/ 匹配 , !, . |
\s |
匹配任何空白字符 (空格、制表符、换行符等) | /\s/ 匹配 |
\S |
匹配任何非空白字符 | /\S/ 匹配 a, 1 |
\b |
匹配单词边界 | /\bcat\b/ 匹配 cat 但不匹配 catamaran |
\B |
匹配非单词边界 | /\Bcat\B/ 匹配 scatter 中的 cat |
^ |
匹配行的开头 | /^hello/ 匹配以 hello 开头的行 |
$ |
匹配行的结尾 | /world$/ 匹配以 world 结尾的行 |
字符类
使用方括号 [] 可以定义一个字符集,匹配方括号中的任意一个字符。
“`javascript
const regex = /[aeiou]/; // 匹配任何一个小写元音字母
console.log(regex.test(“apple”)); // true
console.log(regex.test(“sky”)); // false
const regex2 = /[0-9A-F]/; // 匹配任何一个数字或大写十六进制字母
“`
[a-z]:匹配任何小写字母。[A-Z]:匹配任何大写字母。[0-9]:匹配任何数字。[a-zA-Z0-9]:匹配任何字母或数字。[^abc]:匹配除a、b、c之外的任何字符 (否定字符集)。
量词
量词用于指定一个模式出现的次数。
| 量词 | 描述 | 示例 |
|---|---|---|
* |
匹配零次或多次 | /a*/ 匹配 `,a,aa` |
+ |
匹配一次或多次 | /a+/ 匹配 a, aa 但不匹配 “ |
? |
匹配零次或一次 | /a?/ 匹配 `,a` |
{n} |
匹配恰好 n 次 |
/a{3}/ 匹配 aaa |
{n,} |
匹配至少 n 次 |
/a{2,}/ 匹配 aa, aaa |
{n,m} |
匹配 n 到 m 次 |
/a{1,3}/ 匹配 a, aa, aaa |
贪婪与非贪婪匹配
默认情况下,量词是贪婪的,它们会尽可能多地匹配字符。在量词后添加 ? 可以使其变为非贪婪的(或称惰性匹配),尽可能少地匹配字符。
“`javascript
const html = “
Title
Paragraph
“;
const greedyRegex = /<.*>/; // 贪婪匹配:匹配整个 “
Title
Paragraph
”
console.log(html.match(greedyRegex)); // [“
Title
Paragraph
“]
const lazyRegex = /<.?>/; // 非贪婪匹配:匹配 “<h1”, “
”
console.log(html.match(lazyRegex)); // [“
“] (因为match只返回第一个)
console.log(html.match(/<.?>/g)); // [“
“, “
“, “
“, “
“] (使用g标志)
“`
常用标志 (Flags)
| 标志 | 描述 |
|---|---|
g |
全局匹配 (Global),查找所有匹配而非仅第一个 |
i |
忽略大小写 (Case-insensitive) |
m |
多行模式 (Multiline),^ 和 $ 匹配每行的开头和结尾 |
s |
. 匹配包括换行符在内的所有字符 (dotAll) |
u |
Unicode 模式,支持处理 Unicode 字符 |
y |
粘性模式 (Sticky),从目标字符串的当前位置开始匹配 |
JavaScript 中的 RegExp 方法和 String 方法
JavaScript 提供了 RegExp 对象的方法和 String 对象的方法来操作正则表达式。
RegExp 对象方法:
-
regex.test(str):- 检查字符串中是否存在匹配项。
- 返回
true或false。
javascript
const regex = /apple/;
console.log(regex.test("I have an apple.")); // true -
regex.exec(str):- 在一个字符串中执行查找匹配。
- 如果找到匹配,返回一个结果数组(包含匹配项、捕获组等信息),并更新
lastIndex属性(如果设置了g标志)。 - 如果没有找到匹配,返回
null。 - 结合
g标志,可以循环遍历所有匹配项。
javascript
const str = "apple banana apple";
const regex = /apple/g;
let match;
while ((match = regex.exec(str)) !== null) {
console.log(`Found ${match[0]} at index ${match.index}`);
}
// Found apple at index 0
// Found apple at index 13
String 对象方法:
-
str.match(regex):- 使用正则表达式对字符串进行匹配。
- 如果正则表达式没有
g标志,返回的结果与regex.exec(str)类似(第一个匹配的结果数组)。 - 如果正则表达式有
g标志,返回所有匹配项组成的数组,没有捕获组信息。如果没有匹配项,返回null。
javascript
const str = "The quick brown fox jumps over the lazy dog.";
console.log(str.match(/quick/)); // ["quick", index: 4, ...]
console.log(str.match(/[aeiou]/g)); // ["e", "u", "i", "o", "o", "e", "e", "a", "o"]
console.log(str.match(/xyz/)); // null -
str.search(regex):- 搜索字符串中第一个匹配项的索引。
- 如果没有找到匹配项,返回
-1。
javascript
const str = "Hello World";
console.log(str.search(/World/)); // 6
console.log(str.search(/Mars/)); // -1 -
str.replace(regex|substr, newSubstr|function):- 替换字符串中匹配正则表达式的部分。
- 如果没有
g标志,只替换第一个匹配项。 newSubstr可以包含特殊字符如$。function可以是一个函数,用于动态生成替换文本。
“`javascript
const str = “apple, banana, cherry”;
console.log(str.replace(/banana/, “orange”)); // “apple, orange, cherry”
console.log(str.replace(/a/g, “X”)); // “Xpple, bXnXnX, cherry”// 使用函数替换
const price = “Price: $10.99”;
console.log(price.replace(/\$(\d+.\d+)/, (match, p1) =>¥${parseFloat(p1) * 7}));
// 假设汇率是7,输出 “Price: ¥76.93”
“` -
str.split(regex|separator):- 使用正则表达式或字符串作为分隔符,将字符串分割成数组。
javascript
const str = "one, two; three four";
console.log(str.split(/[,; ]+/)); // ["one", "two", "three", "four"]
第二部分:进阶篇 – 掌握更强大的功能
捕获组 (Capturing Groups)
使用圆括号 () 可以创建捕获组。捕获组不仅可以将匹配的部分组合起来,还可以提取匹配的子字符串。
“`javascript
const url = “https://www.example.com/path/to/page.html”;
const regex = /(https?):\/\/([^\/]+)(.*)/;
const match = url.match(regex);
if (match) {
console.log(“Full match:”, match[0]); // https://www.example.com/path/to/page.html
console.log(“Protocol:”, match[1]); // https
console.log(“Domain:”, match[2]); // www.example.com
console.log(“Path:”, match[3]); // /path/to/page.html
}
“`
在 replace 方法中,可以通过 $1, $2 等引用捕获组的内容。
javascript
const name = "John Doe";
const swappedName = name.replace(/(\w+)\s(\w+)/, "$2, $1");
console.log(swappedName); // Doe, John
非捕获组 (Non-capturing Groups)
如果你只想将某些部分组合起来进行量词操作,但又不想捕获它们,可以使用 (?:...) 创建非捕获组。这有助于提高性能和减少内存占用,因为它们不会存储子匹配。
javascript
const str = "apple banana cherry";
const regex = /(?:apple|banana)/g; // 匹配 apple 或 banana,但不捕获
console.log(str.match(regex)); // ["apple", "banana"]
反向引用 (Backreferences)
反向引用允许你在正则表达式的后面部分引用前面捕获组匹配到的内容。在 JavaScript 中,它们通常用 \1, \2 等表示。
javascript
const str = "She sells seashells by the seashore.";
const regex = /(\w+)\s\1/; // 查找重复的单词
console.log(str.match(regex)); // ["sells seashells", "sells", index: 4, ...]
这里 \1 引用了第一个捕获组 (\w+) 匹配到的内容。
前瞻 (Lookahead) 和 后瞻 (Lookbehind)
前瞻和后瞻是零宽断言(Zero-width assertions),它们匹配一个位置,而不是实际的字符。它们断言在当前位置的后面或前面是否存在某个模式。
-
肯定前瞻 (Positive Lookahead):
(?=pattern)- 匹配后面紧跟着
pattern的位置。
javascript
const str = "I love JavaScript.";
const regex = /love(?=\sJavaScript)/; // 匹配 "love",但只有当它后面跟着 " JavaScript" 时
console.log(str.match(regex)); // ["love", index: 2, ...] - 匹配后面紧跟着
-
否定前瞻 (Negative Lookahead):
(?!pattern)- 匹配后面没有紧跟着
pattern的位置。
javascript
const str = "I love Java. I also love JavaScript.";
const regex = /love(?!Script)/g; // 匹配 "love",但当它后面没有跟着 "Script" 时
console.log(str.match(regex)); // ["love", index: 2, ...] (匹配"love Java"中的love) - 匹配后面没有紧跟着
-
肯定后瞻 (Positive Lookbehind):
(?<=pattern)(ES2018+)- 匹配前面紧跟着
pattern的位置。
javascript
const str = "$100 €50";
const regex = /(?<=\$)\d+/; // 匹配前面是 "$" 符号的数字
console.log(str.match(regex)); // ["100", index: 1, ...] - 匹配前面紧跟着
-
否定后瞻 (Negative Lookbehind):
(?<!pattern)(ES2018+)- 匹配前面没有紧跟着
pattern的位置。
javascript
const str = "Apple Pie, Pineapple Juice";
const regex = /(?<!Pine)apple/g; // 匹配 "apple",但当它前面没有跟着 "Pine" 时
console.log(str.match(regex)); // ["apple", index: 0, ...] - 匹配前面没有紧跟着
Unicode 属性转义 (ES2018+)
使用 \p{UnicodeProperty} 和 \P{UnicodeProperty} (当使用 u 标志时) 可以匹配具有特定 Unicode 属性的字符。
javascript
const str = "你好 world! 👋";
// 匹配任何 Unicode 字母
const letters = str.match(/\p{L}/gu); // ["你", "好", "w", "o", "r", "l", "d"]
// 匹配任何 Unicode 符号
const symbols = str.match(/\p{S}/gu); // ["👋"]
第三部分:实战演练 – 常见应用场景
1. 邮箱验证
“`javascript
function isValidEmail(email) {
// 一个相对健壮的邮箱正则,但实际生产环境可能需要更复杂的
const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
return regex.test(email);
}
console.log(isValidEmail(“[email protected]”)); // true
console.log(isValidEmail(“invalid-email”)); // false
console.log(isValidEmail(“[email protected]”)); // true
“`
2. URL 提取
“`javascript
function extractUrls(text) {
const regex = /(https?:\/\/[^\s]+)/g;
return text.match(regex) || [];
}
const text = “Visit our website at https://www.example.com or our blog at http://blog.example.org”;
console.log(extractUrls(text));
// [“https://www.example.com”, “http://blog.example.org”]
“`
3. 日期格式化 (MM/DD/YYYY 转 YYYY-MM-DD)
“`javascript
function formatDate(dateStr) {
const regex = /(\d{2})\/(\d{2})\/(\d{4})/; // MM/DD/YYYY
return dateStr.replace(regex, “$3-$1-$2”);
}
console.log(formatDate(“12/25/2023”)); // “2023-12-25”
“`
4. 移除 HTML 标签
“`javascript
function stripHtmlTags(html) {
const regex = /<[^>]*>/g;
return html.replace(regex, “”);
}
const html = “
Title
This is a paragraph.
“;
console.log(stripHtmlTags(html)); // “TitleThis is a paragraph.”
“`
5. 密码强度验证 (至少8位,包含大小写字母、数字和特殊字符)
“`javascript
function isStrongPassword(password) {
const minLength = /.{8,}/; // 至少8位
const hasUppercase = /[A-Z]/; // 包含大写字母
const hasLowercase = /[a-z]/; // 包含小写字母
const hasNumber = /[0-9]/; // 包含数字
const hasSpecialChar = /[!@#$%^&*(),.?”:{}|<>]/; // 包含特殊字符
return (
minLength.test(password) &&
hasUppercase.test(password) &&
hasLowercase.test(password) &&
hasNumber.test(password) &&
hasSpecialChar.test(password)
);
}
console.log(isStrongPassword(“P@ssword123”)); // true
console.log(isStrongPassword(“password”)); // false (缺少大写、数字、特殊字符)
console.log(isStrongPassword(“Password123”)); // false (缺少特殊字符)
“`
总结
JavaScript 正则表达式是处理字符串的瑞士军刀。从基本的元字符和量词,到高级的捕获组、前瞻后瞻和 Unicode 属性,掌握这些工具将极大地提升你在文本处理方面的能力。
记住,实践是最好的老师。尝试在不同的场景下使用正则表达式,并不断查阅文档和参考资料,你将很快成为正则表达式的专家!
“`