手把手学习 JavaScript 正则表达式

181 阅读7分钟

什么是正则表达式?

正则表达式(RegEx)是一个特殊的字符序列,用于匹配字符串中的模式。你可以使用正则表达式来描述一个字符串的规则,从而对其进行搜索、验证或替换。

JavaScript 提供了两种方式来创建正则表达式:

  1. 字面量语法/pattern/
  2. 构造函数语法new RegExp("pattern")

在大多数情况下,我们使用字面量语法,它更加简洁。

正则表达式的基本语法

1. 元字符(Metacharacters)

元字符是正则表达式中具有特殊意义的字符。以下是一些常见的元字符:

元字符说明示例
.匹配任意字符(除了换行符)a.c
^匹配字符串的开始^abc
$匹配字符串的结束abc$
[]匹配字符集中的任意字符[aeiou]
``逻辑“或”运算符,匹配左边或右边的模式
()分组,表示子表达式(abc)+
``转义字符,用来匹配特殊字符,如 . 来匹配点号.

2. 字符类(Character Classes)

字符类用于匹配一组字符中的任何一个。例如:

字符类说明示例
\d匹配任何数字,等价于 [0-9]\d{3}
\D匹配任何非数字字符,等价于 [^0-9]\D+
\w匹配任何字母、数字或下划线,等价于 [a-zA-Z0-9_]\w+
\W匹配任何非字母、数字或下划线的字符\W+
\s匹配任何空白字符(空格、制表符、换行符等)\s+
\S匹配任何非空白字符\S+

3. 量词(Quantifiers)

量词用于指定前一个字符应当匹配的次数。常用的量词如下:

量词说明示例
*匹配前一个字符零次或多次a*
+匹配前一个字符一次或多次a+
?匹配前一个字符零次或一次a?
{n}匹配前一个字符恰好出现 n 次a{3}
{n,}匹配前一个字符至少出现 n 次a{2,}
{n,m}匹配前一个字符出现 n 至 m 次之间的次数a{2,4}

4. 边界匹配(Anchors)

边界匹配用于定位字符串的位置,而不是字符本身。

边界符号说明示例
^匹配字符串的开始^abc
$匹配字符串的结束abc$
\b匹配单词边界(单词与非单词字符之间)\bword\b
\B匹配非单词边界\Bword\B

常用正则表达式 API

在 JavaScript 中,有几个方法和正则表达式配合使用,可以让我们非常方便地进行字符串匹配和操作。

1. test()

test() 方法用于测试正则表达式是否与给定字符串匹配。如果匹配,返回 true;否则,返回 false

const regex = /abc/;
console.log(regex.test("abcdef"));  // true
console.log(regex.test("xyz"));     // false

2. exec()

exec() 方法执行正则表达式匹配,并返回一个包含匹配结果的数组。如果没有匹配项,返回 null

const regex = /a(b)c/;
const result = regex.exec("abc");
console.log(result);  // ["abc", "b"]

3. match()

match() 方法用于获取与正则表达式匹配的子字符串。如果没有匹配项,返回 null

const str = "Hello, world!";
const regex = /world/;
console.log(str.match(regex));  // ["world"]

4. replace()

replace() 方法用于替换字符串中的匹配项。

const str = "The quick brown fox";
const result = str.replace(/brown/, "black");
console.log(result);  // "The quick black fox"

5. split()

split() 方法根据正则表达式分割字符串,返回一个数组。

const str = "apple,orange,banana";
const result = str.split(/,/);
console.log(result);  // ["apple", "orange", "banana"]

实际案例

案例 1:邮箱验证

通过正则表达式验证邮箱的有效性。

const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;

function validateEmail(email) {
  return emailRegex.test(email);
}

console.log(validateEmail("test@example.com"));  // true
console.log(validateEmail("test@com"));          // false
console.log(validateEmail("test@.com"));         // false

解释

  • ^[a-zA-Z0-9._%+-]+:匹配邮箱的用户名部分。
  • @[a-zA-Z0-9.-]+:匹配 @ 后的域名。
  • .[a-zA-Z]{2,}$:匹配域名后缀。

案例 2:手机号验证(中国)

检查中国大陆手机号的格式是否符合规则。

const phoneRegex = /^1[3-9]\d{9}$/;

function validatePhoneNumber(phone) {
  return phoneRegex.test(phone);
}

console.log(validatePhoneNumber("13812345678"));  // true
console.log(validatePhoneNumber("12345678901"));  // false
console.log(validatePhoneNumber("1381234567"));   // false

解释

  • ^1[3-9]:手机号以 1 开头,第二位为 3 至 9 之间的任意数字。
  • \d{9}$:后面跟着 9 个数字。

案例 3:提取日期(YYYY-MM-DD 格式)

从文本中提取符合日期格式的所有日期。

const text = "今天是 2025-07-22,明天是 2025-07-23,昨天是 2025-07-21。";
const dateRegex = /\d{4}-\d{2}-\d{2}/g;

const dates = text.match(dateRegex);
console.log(dates);  // ["2025-07-22", "2025-07-23", "2025-07-21"]

解释

  • \d{4}-\d{2}-\d{2}:匹配日期格式 YYYY-MM-DD

案例 4:替换文本中的 URL

在文本中查找并替换所有的网址。

const text = "请访问我们的网站:http://example.com 和 https://openai.com 获取更多信息。";
const urlRegex = /https?://[a-zA-Z0-9.-]+/g;

const newText = text.replace(urlRegex, "[URL]");
console.log(newText);  
// 输出: 请访问我们的网站:[URL] 和 [URL] 获取更多信息。

解释

  • https?:匹配 httphttps,问号表示可选的字符。
  • ://:匹配 ://,斜杠是转义字符。
  • [a-zA-Z0-9.-]+:匹配域名部分,允许字母、数字、点和连字符。

案例 5:提取 HTML 标签中的属性

我们可以通过正则表达式从 HTML 标签中提取属性。例如,我们提取 <img src="image.jpg" alt="An image"> 标签中的 srcalt 属性:

const html = '<img src="image.jpg" alt="An image">';
const regex = /(\w+)="([^"]+)"/g;

let match;
const attributes = {};
while ((match = regex.exec(html)) !== null) {
  attributes[match[1]] = match[2];  // match[1] 是属性名,match[2] 是属性值
}

console.log(attributes);
// 输出: { src: "image.jpg", alt: "An image" }

解释

  • (\w+):匹配属性名,使用捕获组捕获。
  • ="([^"]+)":匹配等号后面的属性值,确保是用双引号括起来的内容。
  • g:全局匹配,表示查找所有匹配项。

案例 6:提取字符串中的数字

从一段文本中提取所有的数字,例如 "我有2个苹果,3个橙子和5个香蕉。"

const str = "我有2个苹果,3个橙子和5个香蕉。";
const numberRegex = /\d+/g;

const numbers = str.match(numberRegex);
console.log(numbers);  // ["2", "3", "5"]

解释

  • \d+:匹配一个或多个数字,g 表示全局匹配所有数字。

案例 7:查找重复的单词

查找文本中是否有重复出现的单词,使用正则表达式来实现:

const text = "这是一个重复重复的文本。";
const duplicateWordRegex = /\b(\w+)\s+\1\b/g;

const result = text.match(duplicateWordRegex);
console.log(result);  // ["重复重复"]

解释

  • \b(\w+)\b:匹配一个单词,并将其捕获到第一个捕获组。
  • \s+:匹配一个或多个空白字符。
  • \1:引用第一个捕获的单词,表示重复出现的相同单词。

进阶应用:断言与零宽断言

JavaScript 的正则表达式还支持一些进阶应用,如断言(Assertions)和零宽断言(Zero-width Assertions),可以帮助我们在匹配时不消耗字符。

1. 正向先行断言(Positive Lookahead)

正向先行断言用于检查某个模式后面是否跟着另一个模式,但不消耗字符。常用于验证某个字符后面必须跟着特定的字符。

const regex = /\d(?=\D)/;
console.log("abc1".match(regex));  // ["1"]

解释

  • \d:匹配一个数字。
  • (?=\D):正向先行断言,确保数字后面跟着一个非数字字符(即非数字字符跟在数字后面)。

2. 负向先行断言(Negative Lookahead)

负向先行断言用于检查某个模式后面是否没有另一个模式,也不消耗字符。

const regex = /\d(?!\d)/;
console.log("abc123".match(regex));  // ["3"]

解释

  • \d:匹配一个数字。
  • (?!\d):负向先行断言,确保数字后面不跟着另一个数字。

3. 零宽反向断言(Negative Lookbehind)

零宽反向断言用于确保某个模式前面没有另一个模式,常用于匹配前面的字符,而不包括前面不需要的字符。

const regex = /(?<!\d)\D+/;
console.log("abc123".match(regex));  // ["abc"]

解释

  • (?<!\d):负向后顾断言,确保匹配的部分前面没有数字。
  • \D+:匹配一个或多个非数字字符。

总结

JavaScript 正则表达式是处理字符串和文本的强大工具,掌握它可以帮助你高效地进行文本验证、匹配和替换操作。通过掌握基本的正则语法、常用的 API 和一些高级应用,你可以在开发中轻松应对各种文本处理需求。

以下是学习 JavaScript 正则表达式的几个要点:

  1. 理解基本语法:正则表达式的元字符、字符类、量词和边界匹配符是构建模式的基础。
  2. 熟悉常用 API:掌握 test()exec()match()replace()split() 等方法,将使你能够高效地应用正则表达式。
  3. 灵活运用案例:通过实际的案例学习如何在现实项目中应用正则表达式,包括表单验证、提取信息、替换文本等。
  4. 探索高级特性:学会使用断言、零宽断言等高级特性,提升正则表达式的灵活性和精确度。

通过不断练习和探索,你会发现正则表达式在 JavaScript 开发中的强大潜力。希望本文对你学习和掌握 JavaScript 正则表达式有所帮助,提升你在开发中的能力!