Skip to content

JavaScript RegExp 对象

什么是正则表达式

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

正则表达式用于:

  • 搜索字符串
  • 替换字符串
  • 提取字符串
  • 验证字符串格式

正则表达式的创建

你可以通过以下两种方式创建正则表达式:

字面量语法

使用斜杠 (/) 包围模式:

javascript
// 字面量语法
let regex1 = /pattern/flags;

// 示例:匹配所有包含 "hello" 的字符串
let regex2 = /hello/;

// 示例:匹配所有包含 "hello" 的字符串,忽略大小写
let regex3 = /hello/i;

构造函数语法

使用 RegExp 构造函数:

javascript
// 构造函数语法
let regex1 = new RegExp("pattern", "flags");

// 示例:匹配所有包含 "hello" 的字符串
let regex2 = new RegExp("hello");

// 示例:匹配所有包含 "hello" 的字符串,忽略大小写
let regex3 = new RegExp("hello", "i");

// 示例:使用变量作为模式
let pattern = "hello";
let regex4 = new RegExp(pattern);

正则表达式的模式

字符类

模式描述
[abc]匹配方括号内的任意一个字符
[^abc]匹配除了方括号内的任意一个字符
[a-z]匹配 a 到 z 范围内的任意一个字符
[A-Z]匹配 A 到 Z 范围内的任意一个字符
[0-9]匹配 0 到 9 范围内的任意一个数字
[a-zA-Z0-9]匹配字母和数字

元字符

元字符描述
.匹配除换行符以外的任意单个字符
\d匹配一个数字字符,等价于 [0-9]
\D匹配一个非数字字符,等价于 [^0-9]
\w匹配一个字母、数字或下划线,等价于 [a-zA-Z0-9_]
\W匹配一个非字母、数字或下划线,等价于 [^a-zA-Z0-9_]
\s匹配一个空白字符(空格、制表符、换行符等)
\S匹配一个非空白字符
\b匹配单词边界
\B匹配非单词边界
^匹配字符串的开始
$匹配字符串的结束

量词

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

分组和捕获

语法描述
(x)捕获组,匹配 x 并记住匹配项
(?:x)非捕获组,匹配 x 但不记住匹配项
(?=x)正向先行断言,匹配后面跟着 x 的位置
(?!x)负向先行断言,匹配后面不跟着 x 的位置

标志

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

RegExp 对象的方法

1. test()

test() 方法用于测试字符串是否匹配正则表达式,返回布尔值。

javascript
let regex = /hello/;
console.log(regex.test("hello world")); // true
console.log(regex.test("hi world")); // false

// 使用全局标志
let regexGlobal = /hello/g;
console.log(regexGlobal.test("hello hello")); // true
console.log(regexGlobal.lastIndex); // 5(下一次匹配的开始位置)
console.log(regexGlobal.test("hello hello")); // true
console.log(regexGlobal.lastIndex); // 11
console.log(regexGlobal.test("hello hello")); // false(没有更多匹配项)
console.log(regexGlobal.lastIndex); // 0(重置为0)

2. exec()

exec() 方法用于在字符串中执行正则表达式匹配,返回匹配结果数组或 null

javascript
let regex = /hello/;
console.log(regex.exec("hello world")); // ['hello', index: 0, input: 'hello world', groups: undefined]
console.log(regex.exec("hi world")); // null

// 使用捕获组
let regexWithGroups = /(h)(e)(l)(l)(o)/;
console.log(regexWithGroups.exec("hello world"));
// ['hello', 'h', 'e', 'l', 'l', 'o', index: 0, input: 'hello world', groups: undefined]

// 使用全局标志
let regexGlobal = /hello/g;
console.log(regexGlobal.exec("hello hello")); // ['hello', index: 0, input: 'hello hello', groups: undefined]
console.log(regexGlobal.exec("hello hello")); // ['hello', index: 6, input: 'hello hello', groups: undefined]
console.log(regexGlobal.exec("hello hello")); // null

字符串对象的正则方法

1. match()

match() 方法用于在字符串中执行正则表达式匹配,返回匹配结果数组或 null

javascript
let str = "hello world";
let regex = /hello/;
console.log(str.match(regex)); // ['hello', index: 0, input: 'hello world', groups: undefined]

// 使用全局标志
let regexGlobal = /o/g;
console.log(str.match(regexGlobal)); // ['o', 'o']

// 使用捕获组
let str2 = "hello world";
let regexWithGroups = /(h)(e)(l)(l)(o)/;
console.log(str2.match(regexWithGroups));
// ['hello', 'h', 'e', 'l', 'l', 'o', index: 0, input: 'hello world', groups: undefined]

2. replace()

replace() 方法用于在字符串中替换匹配的子串,返回替换后的新字符串。

javascript
let str = "hello world";
let regex = /hello/;
console.log(str.replace(regex, "hi")); // 'hi world'

// 使用全局标志
let str2 = "hello hello world";
let regexGlobal = /hello/g;
console.log(str2.replace(regexGlobal, "hi")); // 'hi hi world'

// 使用捕获组
let str3 = "John Doe";
let regexWithGroups = /(\w+) (\w+)/;
console.log(str3.replace(regexWithGroups, "$2, $1")); // 'Doe, John'

// 使用函数作为替换值
let str4 = "hello 123 world 456";
let regexNumbers = /\d+/g;
console.log(str4.replace(regexNumbers, (match) => parseInt(match) * 2)); // 'hello 246 world 912'

search() 方法用于在字符串中搜索匹配的子串,返回第一个匹配项的索引,未找到返回 -1

javascript
let str = "hello world";
let regex = /world/;
console.log(str.search(regex)); // 6

let regex2 = /test/;
console.log(str.search(regex2)); // -1

4. split()

split() 方法用于将字符串分割为数组,返回分割后的数组。

javascript
let str = 'hello,world,test';
let regex = /,/;
console.log(str.split(regex)); // ['hello', 'world', 'test']

// 使用正则表达式分割
let str2 = 'hello   world
test';
let regexWhitespace = /\s+/;
console.log(str2.split(regexWhitespace)); // ['hello', 'world', 'test']

正则表达式的应用场景

1. 表单验证

javascript
// 验证邮箱
function validateEmail(email) {
  const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  return regex.test(email);
}

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

// 验证手机号码
function validatePhone(phone) {
  const regex = /^1[3-9]\d{9}$/;
  return regex.test(phone);
}

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

// 验证密码强度
function validatePassword(password) {
  // 至少8位,包含至少一个字母和一个数字
  const regex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
  return regex.test(password);
}

console.log(validatePassword("Password123")); // true
console.log(validatePassword("password")); // false
console.log(validatePassword("12345678")); // false

2. 字符串处理

javascript
// 提取URL中的域名
function extractDomain(url) {
  const regex = /^(?:https?:\/\/)?(?:www\.)?([^\/]+)/i;
  const match = url.match(regex);
  return match ? match[1] : null;
}

console.log(extractDomain("https://www.example.com/path")); // 'example.com'
console.log(extractDomain("http://example.com")); // 'example.com'
console.log(extractDomain("example.com")); // 'example.com'

// 格式化日期
function formatDate(dateString) {
  // 将 YYYY-MM-DD 格式转换为 DD/MM/YYYY
  const regex = /^(\d{4})-(\d{2})-(\d{2})$/;
  return dateString.replace(regex, "$3/$2/$1");
}

console.log(formatDate("2023-10-05")); // '05/10/2023'

// 移除HTML标签
function removeHtmlTags(html) {
  const regex = /<[^>]+>/g;
  return html.replace(regex, "");
}

console.log(removeHtmlTags("<p>Hello <b>world</b></p>")); // 'Hello world'

3. 数据提取

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

console.log(extractNumbers("There are 123 apples and 456 oranges.")); // [123, 456]

// 提取引号中的内容
function extractQuotedText(text) {
  const regex = /"([^"]*)"/g;
  const matches = [];
  let match;
  while ((match = regex.exec(text)) !== null) {
    matches.push(match[1]);
  }
  return matches;
}

console.log(extractQuotedText('He said "hello" and she said "hi".')); // ['hello', 'hi']

正则表达式的性能

性能优化技巧

  1. 避免过度使用正则表达式:对于简单的字符串操作,使用字符串方法(如 includes(), startsWith(), endsWith())可能比正则表达式更快。

  2. 使用具体的模式:越具体的正则表达式模式,匹配速度越快。避免使用过于宽泛的模式,如 .*

  3. 使用非捕获组:对于不需要捕获的分组,使用非捕获组 (?:x) 可以提高性能。

  4. 避免回溯:复杂的正则表达式可能会导致大量回溯,影响性能。例如,避免使用 (a*)*b 这样的模式。

  5. 使用粘性标志 y:当你需要从特定位置开始匹配时,使用粘性标志 y 可以提高性能。

  6. 缓存正则表达式:对于重复使用的正则表达式,将其存储在变量中,避免重复创建。

性能测试

javascript
// 测试字符串方法 vs 正则表达式
const text = "hello world";

// 测试 includes() 方法
console.time("includes");
for (let i = 0; i < 1000000; i++) {
  text.includes("hello");
}
console.timeEnd("includes");

// 测试正则表达式 test() 方法
const regex = /hello/;
console.time("regex");
for (let i = 0; i < 1000000; i++) {
  regex.test(text);
}
console.timeEnd("regex");

最佳实践

  1. 选择合适的创建方式

    • 对于固定的模式,使用字面量语法 /pattern/
    • 对于动态的模式,使用构造函数语法 new RegExp(pattern)
  2. 使用适当的标志:根据需要使用 gim 等标志。

  3. 注意转义字符:在正则表达式中,某些字符具有特殊含义,需要使用反斜杠 \ 转义。

  4. 处理全局标志的副作用:使用全局标志 g 时,正则表达式对象会跟踪 lastIndex 属性,可能导致意外的行为。

  5. 使用命名捕获组:ES2018 引入了命名捕获组,可以使代码更具可读性。

    javascript
    let regex = /(?<firstName>\w+) (?<lastName>\w+)/;
    let match = regex.exec("John Doe");
    console.log(match.groups.firstName); // 'John'
    console.log(match.groups.lastName); // 'Doe'
  6. 使用正则表达式测试工具:使用在线工具(如 regex101.com)测试和调试正则表达式。

  7. 添加注释:对于复杂的正则表达式,添加注释说明其功能和各部分的含义。

    javascript
    // 使用注释模式(需要 u 标志)
    let regex = /^\d{4}-\d{2}-\d{2}$/u;
    
    // 或者使用多行注释
    let regexWithComments = new RegExp(
      "^" + // 字符串开始
        "\\d{4}" + // 年(4位数字)
        "-" + // 分隔符
        "\\d{2}" + // 月(2位数字)
        "-" + // 分隔符
        "\\d{2}" + // 日(2位数字)
        "$" // 字符串结束
    );

总结

RegExp 对象是 JavaScript 中处理字符串匹配和操作的强大工具。它提供了丰富的语法和方法来处理各种字符串模式匹配任务。了解正则表达式的语法和用法,有助于你更有效地处理 JavaScript 中的字符串操作。

通过合理使用正则表达式和遵循最佳实践,你可以编写更简洁、高效的 JavaScript 代码来处理各种字符串匹配和操作任务。