Skip to content

正则表达式

正则表达式的概念

正则表达式(Regular Expression,简称 regex 或 regexp)是一种用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。

正则表达式的创建

1. 使用正则表达式字面量

javascript
const regex = /pattern/flags;

// 示例
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const phoneRegex = /^1[3-9]\d{9}$/;

2. 使用 RegExp 构造函数

javascript
const regex = new RegExp('pattern', 'flags');

// 示例
const emailRegex = new RegExp('^[^\s@]+@[^\s@]+\.[^\s@]+$');
const phoneRegex = new RegExp('^1[3-9]\\d{9}$');

正则表达式的标志(Flags)

标志描述
g全局搜索,找到所有匹配项
i忽略大小写
m多行模式,^ 和 $ 匹配每一行的开始和结束
s允许 . 匹配换行符
uUnicode 模式,正确处理 Unicode 字符
y粘性匹配,从目标字符串的当前位置开始匹配

正则表达式的元字符

字符类

元字符描述
.匹配除换行符以外的任意字符
\d匹配数字,等价于 [0-9]
\D匹配非数字,等价于 [^0-9]
\w匹配字母、数字、下划线,等价于 [a-zA-Z0-9_]
\W匹配非字母、数字、下划线,等价于 [^a-zA-Z0-9_]
\s匹配空白字符(空格、制表符、换行符等)
\S匹配非空白字符

边界匹配

元字符描述
^匹配字符串的开始
$匹配字符串的结束
\b匹配单词边界
\B匹配非单词边界

量词

元字符描述
*匹配前面的表达式 0 次或多次,等价于
+匹配前面的表达式 1 次或多次,等价于
?匹配前面的表达式 0 次或 1 次,等价于
{n}匹配前面的表达式恰好 n 次
{n,}匹配前面的表达式至少 n 次
{n,m}匹配前面的表达式至少 n 次,最多 m 次

贪婪与非贪婪

  • 贪婪匹配:默认情况下,量词会尽可能多地匹配字符
  • 非贪婪匹配:在量词后添加 ? 可以实现非贪婪匹配
javascript
const str = 'aaaa';

// 贪婪匹配
console.log(str.match(/a+/)); // 输出: ['aaaa']

// 非贪婪匹配
console.log(str.match(/a+?/)); // 输出: ['a']

分组与捕获

元字符描述
(x)捕获组,匹配 x 并记住匹配项
(?:x)非捕获组,匹配 x 但不记住匹配项
\n反向引用,引用第 n 个捕获组
javascript
const str = '123-456-7890';

// 捕获组
const regex = /(\d{3})-(\d{3})-(\d{4})/;
const match = str.match(regex);
console.log(match[0]); // 输出: '123-456-7890'
console.log(match[1]); // 输出: '123'
console.log(match[2]); // 输出: '456'
console.log(match[3]); // 输出: '7890'

// 反向引用
const regex2 = /(\d{3})-\1/;
console.log(regex2.test('123-123')); // 输出: true
console.log(regex2.test('123-456')); // 输出: false

选择

元字符描述
x|y匹配 x 或 y
javascript
const regex = /apple|banana|orange/;
console.log(regex.test('apple')); // 输出: true
console.log(regex.test('banana')); // 输出: true
console.log(regex.test('orange')); // 输出: true
console.log(regex.test('grape')); // 输出: false

转义字符

元字符描述
\转义字符,用于匹配特殊字符本身
javascript
const regex = /\.\*\+/; // 匹配 .*+
console.log(regex.test('.*+')); // 输出: true

正则表达式的方法

RegExp 对象的方法

1. test()

检查字符串是否匹配正则表达式,返回布尔值。

javascript
const regex = /^\d+$/;
console.log(regex.test('123')); // 输出: true
console.log(regex.test('abc')); // 输出: false

2. exec()

在字符串中执行正则表达式匹配,返回匹配结果数组或 null。

javascript
const regex = /(\d{3})-(\d{4})/g;
const str = '123-4567, 890-1234';

let match;
while ((match = regex.exec(str)) !== null) {
  console.log(`匹配: ${match[0]}, 区号: ${match[1]}, 号码: ${match[2]}`);
  console.log(`下一次匹配从索引 ${regex.lastIndex} 开始`);
}

字符串的方法

1. match()

在字符串中搜索匹配正则表达式的部分,返回匹配结果数组或 null。

javascript
const str = 'Hello 123 World 456';
const regex = /\d+/g;

console.log(str.match(regex)); // 输出: ['123', '456']

2. replace()

替换字符串中匹配正则表达式的部分,返回替换后的字符串。

javascript
const str = 'Hello World';
const regex = /World/;

console.log(str.replace(regex, 'JavaScript')); // 输出: 'Hello JavaScript'

// 使用捕获组
const str2 = '123-456-7890';
const regex2 = /(\d{3})-(\d{3})-(\d{4})/;
console.log(str2.replace(regex2, '$1$2$3')); // 输出: '1234567890'

// 使用函数
const str3 = 'Hello 123 World 456';
const regex3 = /\d+/g;
console.log(str3.replace(regex3, match => parseInt(match) * 2)); // 输出: 'Hello 246 World 912'

在字符串中搜索匹配正则表达式的部分,返回第一个匹配的索引,没有匹配则返回 -1。

javascript
const str = 'Hello World';
const regex = /World/;

console.log(str.search(regex)); // 输出: 6

4. split()

根据正则表达式分割字符串,返回分割后的数组。

javascript
const str = 'Hello, World! How are you?';
const regex = /\s*[,!?.]\s*/;

console.log(str.split(regex)); // 输出: ['Hello', 'World', 'How are you', '']

常见正则表达式模式

1. 邮箱地址

javascript
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

2. 手机号码(中国大陆)

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

3. 身份证号码(中国大陆)

javascript
const idCardRegex = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;

4. URL

javascript
const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;

5. 密码强度

要求:至少8位,包含大小写字母、数字和特殊字符

javascript
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

6. IPv4 地址

javascript
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

7. 日期格式(YYYY-MM-DD)

javascript
const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;

正则表达式的性能优化

  1. 避免过度使用正则表达式:对于简单的字符串操作,使用字符串方法可能更高效
  2. 使用具体的模式:避免使用过于宽泛的模式,如 .*
  3. 使用非捕获组:对于不需要捕获的分组,使用 (?:x) 而不是 (x)
  4. 使用粘性匹配:对于多次匹配同一字符串,使用 y 标志可以提高性能
  5. 避免回溯:设计正则表达式时,尽量避免导致大量回溯的模式

正则表达式的实际应用

1. 表单验证

javascript
function validateForm() {
  const email = document.getElementById('email').value;
  const phone = document.getElementById('phone').value;
  
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const phoneRegex = /^1[3-9]\d{9}$/;
  
  if (!emailRegex.test(email)) {
    alert('请输入有效的邮箱地址');
    return false;
  }
  
  if (!phoneRegex.test(phone)) {
    alert('请输入有效的手机号码');
    return false;
  }
  
  return true;
}

2. 文本处理

javascript
// 提取文本中的所有数字
function extractNumbers(text) {
  const regex = /\d+/g;
  return text.match(regex) || [];
}

// 替换文本中的敏感词
function censorText(text, sensitiveWords) {
  const regex = new RegExp(sensitiveWords.join('|'), 'gi');
  return text.replace(regex, '*'.repeat(5));
}

3. 数据提取

javascript
// 从 URL 中提取查询参数
function getQueryParams(url) {
  const params = {};
  const regex = /[?&]([^=&]+)=([^&]*)/g;
  let match;
  
  while ((match = regex.exec(url)) !== null) {
    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
  }
  
  return params;
}

正则表达式的局限性

  1. 性能问题:复杂的正则表达式可能导致性能下降
  2. 可读性:复杂的正则表达式难以理解和维护
  3. 功能限制:某些复杂的模式可能无法用正则表达式完全表达
  4. 跨语言差异:不同编程语言的正则表达式语法可能有差异

总结

正则表达式是一种强大的工具,用于匹配、搜索、替换和提取字符串中的模式。在 JavaScript 中,正则表达式可以通过字面量或 RegExp 构造函数创建,并使用各种元字符和标志来定义匹配模式。

掌握正则表达式对于前端开发非常重要,它可以帮助我们:

  1. 验证用户输入
  2. 处理和转换文本
  3. 提取数据
  4. 进行复杂的字符串操作

虽然正则表达式有时看起来复杂,但通过理解其基本原理和常见模式,我们可以有效地使用它来解决各种字符串处理问题。