正则表达式
本章将详细介绍正则表达式的基本语法和在JavaScript中的应用,包括模式匹配、字符串验证、文本替换等常用操作,帮助您掌握这一强大的文本处理工具。
14.1 正则表达式概述
正则表达式是一种用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式通过RegExp对象来表示和操作。
正则表达式的优势
- 模式匹配:可以精确匹配复杂的字符串模式
- 文本验证:验证输入是否符合特定格式
- 文本搜索:在文本中查找特定模式
- 文本替换:替换符合模式的文本
- 文本分割:根据模式分割字符串
14.2 正则表达式的创建
在JavaScript中有两种创建正则表达式的方式。
字面量语法
// 字面量语法
let regex1 = /hello/;
let regex2 = /hello/i; // i标志表示忽略大小写
let regex3 = /hello/g; // g标志表示全局匹配
let regex4 = /hello/m; // m标志表示多行匹配
let regex5 = /hello/u; // u标志表示Unicode模式 (ES6)
let regex6 = /hello/y; // y标志表示粘性匹配 (ES6)
构造函数
// RegExp构造函数
let regex1 = new RegExp("hello");
let regex2 = new RegExp("hello", "i"); // 标志作为第二个参数
let regex3 = new RegExp(/hello/i, "g"); // 从现有正则表达式创建 (ES6)
// 动态创建正则表达式
let pattern = "hello";
let flags = "gi";
let dynamicRegex = new RegExp(pattern, flags);
14.3 正则表达式语法
正则表达式由普通字符和特殊字符(元字符)组成。
普通字符
// 普通字符匹配自身
let regex = /hello/;
console.log(regex.test("hello world")); // true
console.log(regex.test("Hello world")); // false (区分大小写)
字符类
// 字符类匹配方括号中的任意一个字符
let regex1 = /[abc]/; // 匹配a、b或c
console.log(regex1.test("apple")); // true
console.log(regex1.test("banana")); // true
console.log(regex1.test("cherry")); // true
console.log(regex1.test("dog")); // false
// 范围字符类
let regex2 = /[a-z]/; // 匹配任意小写字母
let regex3 = /[A-Z]/; // 匹配任意大写字母
let regex4 = /[0-9]/; // 匹配任意数字
let regex5 = /[a-zA-Z0-9]/; // 匹配任意字母或数字
// 反向字符类
let regex6 = /[^0-9]/; // 匹配任意非数字字符
预定义字符类
// 预定义字符类
let regex1 = /\d/; // 等价于[0-9],匹配数字
let regex2 = /\D/; // 等价于[^0-9],匹配非数字
let regex3 = /\w/; // 等价于[a-zA-Z0-9_],匹配单词字符
let regex4 = /\W/; // 等价于[^a-zA-Z0-9_],匹配非单词字符
let regex5 = /\s/; // 匹配空白字符(空格、制表符、换行符等)
let regex6 = /\S/; // 匹配非空白字符
量词
// 量词指定匹配次数
let regex1 = /a*/; // 匹配0个或多个a
let regex2 = /a+/; // 匹配1个或多个a
let regex3 = /a?/; // 匹配0个或1个a
let regex4 = /a{3}/; // 精确匹配3个a
let regex5 = /a{2,5}/; // 匹配2到5个a
let regex6 = /a{2,}/; // 匹配2个或更多a
// 贪婪匹配与非贪婪匹配
let text = "aaaa";
let greedy = /a+/; // 贪婪匹配
let nonGreedy = /a+?/; // 非贪婪匹配
console.log(text.match(greedy)); // ["aaaa"]
console.log(text.match(nonGreedy)); // ["a"]
定位符
// 定位符匹配位置而非字符
let regex1 = /^hello/; // 匹配行首的hello
let regex2 = /world$/; // 匹配行尾的world
let regex3 = /\bword\b/; // 匹配完整单词word
let regex4 = /\Bword\B/; // 匹配非单词边界的word
console.log(regex1.test("hello world")); // true
console.log(regex1.test("say hello")); // false
console.log(regex2.test("hello world")); // true
console.log(regex2.test("world peace")); // false
分组和捕获
// 分组使用圆括号
let regex = /(hello) (world)/;
let result = "hello world".match(regex);
console.log(result[0]); // "hello world" (完整匹配)
console.log(result[1]); // "hello" (第一个分组)
console.log(result[2]); // "world" (第二个分组)
// 非捕获分组
let regex2 = /(?:hello) (world)/;
let result2 = "hello world".match(regex2);
console.log(result2.length); // 2 (不包含非捕获分组)
// 命名捕获组 (ES2018)
let regex3 = /(?hello) (?world)/;
let result3 = "hello world".match(regex3);
console.log(result3.groups.greet); // "hello"
console.log(result3.groups.name); // "world"
14.4 正则表达式标志
正则表达式标志用于修改匹配行为。
// i标志 - 忽略大小写
let regex1 = /hello/i;
console.log(regex1.test("HELLO")); // true
// g标志 - 全局匹配
let regex2 = /hello/g;
let text = "hello hello hello";
console.log(text.match(regex2)); // ["hello", "hello", "hello"]
// m标志 - 多行匹配
let regex3 = /^hello/m;
let text2 = "say\nhello\nworld";
console.log(regex3.test(text2)); // true
// u标志 - Unicode模式 (ES6)
let regex4 = /\u{61}/u; // Unicode转义
console.log(regex4.test("a")); // true
// y标志 - 粘性匹配 (ES6)
let regex5 = /hello/y;
let text3 = "hello world";
console.log(regex5.test(text3)); // true
regex5.lastIndex = 1;
console.log(regex5.test(text3)); // false (必须从lastIndex位置开始匹配)
14.5 RegExp对象方法
RegExp对象提供了多种方法用于执行匹配操作。
test()方法
// test()方法测试是否匹配
let regex = /hello/;
console.log(regex.test("hello world")); // true
console.log(regex.test("goodbye")); // false
// 全局匹配时的状态
let globalRegex = /hello/g;
console.log(globalRegex.test("hello hello")); // true
console.log(globalRegex.lastIndex); // 5
console.log(globalRegex.test("hello hello")); // true
console.log(globalRegex.lastIndex); // 11
console.log(globalRegex.test("hello hello")); // false
console.log(globalRegex.lastIndex); // 0 (重置)
exec()方法
// exec()方法执行匹配并返回详细信息
let regex = /hello (world)/;
let result = regex.exec("hello world");
console.log(result[0]); // "hello world" (完整匹配)
console.log(result[1]); // "world" (捕获组)
console.log(result.index); // 0 (匹配位置)
console.log(result.input); // "hello world" (输入字符串)
// 全局匹配
let globalRegex = /hello/g;
let text = "hello hello";
let result1 = globalRegex.exec(text);
let result2 = globalRegex.exec(text);
console.log(result1); // 第一个匹配
console.log(result2); // 第二个匹配
14.6 字符串的正则方法
字符串对象也提供了使用正则表达式的方法。
match()方法
// match()方法返回匹配结果
let text = "hello world";
let regex = /hello/;
console.log(text.match(regex)); // ["hello", index: 0, input: "hello world", groups: undefined]
// 全局匹配返回所有匹配
let globalRegex = /l/g;
console.log(text.match(globalRegex)); // ["l", "l", "l"]
// 没有匹配返回null
console.log(text.match(/xyz/)); // null
search()方法
// search()方法返回匹配位置
let text = "hello world";
console.log(text.search(/world/)); // 6
console.log(text.search(/xyz/)); // -1 (未找到)
replace()方法
// replace()方法替换匹配内容
let text = "hello world, hello javascript";
// 替换第一个匹配
console.log(text.replace(/hello/, "hi")); // "hi world, hello javascript"
// 全局替换
console.log(text.replace(/hello/g, "hi")); // "hi world, hi javascript"
// 使用捕获组
let phone = "123-456-7890";
console.log(phone.replace(/(\d{3})-(\d{3})-(\d{4})/, "($1) $2-$3")); // "(123) 456-7890"
// 使用函数替换
let text2 = "The price is $100 and $200";
console.log(text2.replace(/\$(\d+)/g, (match, price) => `$${price * 1.1}`));
// "The price is $110 and $220"
split()方法
// split()方法根据正则表达式分割字符串
let text = "apple,banana;orange:grape";
let separators = /[,;:]/;
console.log(text.split(separators)); // ["apple", "banana", "orange", "grape"]
// 限制分割数量
console.log(text.split(separators, 2)); // ["apple", "banana"]
// 使用捕获组保留分隔符
let text2 = "hello123world456test";
console.log(text2.split(/(\d+)/)); // ["hello", "123", "world", "456", "test"]
14.7 正则表达式高级特性
ES6及后续版本为正则表达式添加了新的特性。
Unicode属性转义 (ES2018)
// Unicode属性转义
let regex1 = /\p{Script=Han}+/u; // 匹配汉字
console.log(regex1.test("你好世界")); // true
let regex2 = /\p{Emoji}+/u; // 匹配表情符号
console.log(regex2.test("Hello 👋 World 🌍")); // true
let regex3 = /\p{Lu}+/u; // 匹配大写字母
console.log(regex3.test("HELLO")); // true
后行断言 (ES2018)
// 后行断言
let regex1 = /(?<=\$)\d+/; // 匹配美元符号后的数字
console.log("价格是$100".match(regex1)); // ["100"]
let regex2 = /(?
dotAll模式 (ES2018)
// dotAll模式
let regex1 = /hello.world/; // .不匹配换行符
console.log(regex1.test("hello\nworld")); // false
let regex2 = /hello.world/s; // s标志使.匹配换行符
console.log(regex2.test("hello\nworld")); // true
14.8 实际应用示例
表单验证
// 邮箱验证
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
console.log(validateEmail("user@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-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
return regex.test(password);
}
console.log(validatePassword("Password123!")); // true
console.log(validatePassword("password")); // false
文本处理
// 提取URL
function extractUrls(text) {
const urlRegex = /https?:\/\/[^\s]+/g;
return text.match(urlRegex) || [];
}
let text = "访问 https://example.com 或 http://test.org 获取更多信息";
console.log(extractUrls(text)); // ["https://example.com", "http://test.org"]
// 格式化电话号码
function formatPhone(phone) {
const cleanPhone = phone.replace(/\D/g, ''); // 移除所有非数字字符
return cleanPhone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}
console.log(formatPhone("13812345678")); // "138-1234-5678"
console.log(formatPhone("138-1234-5678")); // "138-1234-5678"
// 敏感词过滤
function filterSensitiveWords(text, sensitiveWords) {
let filteredText = text;
sensitiveWords.forEach(word => {
const regex = new RegExp(word, 'gi');
filteredText = filteredText.replace(regex, '*'.repeat(word.length));
});
return filteredText;
}
let content = "这篇文章包含敏感词";
let sensitiveWords = ["敏感", "词"];
console.log(filterSensitiveWords(content, sensitiveWords)); // "这篇文章包含**词"
数据解析
// 解析CSV数据
function parseCSV(csvText) {
const lines = csvText.trim().split('\n');
const headers = lines[0].split(',').map(h => h.trim());
return lines.slice(1).map(line => {
const values = line.split(',').map(v => v.trim());
const obj = {};
headers.forEach((header, index) => {
obj[header] = values[index];
});
return obj;
});
}
let csv = `
name,age,city
张三,25,北京
李四,30,上海
`;
console.log(parseCSV(csv));
// 解析日志
function parseLog(logLine) {
const regex = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)$/;
const match = logLine.match(regex);
if (match) {
return {
timestamp: match[1],
level: match[2],
message: match[3]
};
}
return null;
}
let log = "2025-10-11 14:30:45 [INFO] Application started";
console.log(parseLog(log));
// {timestamp: "2025-10-11 14:30:45", level: "INFO", message: "Application started"}
提示:正则表达式是处理文本的强大工具,但也要注意其复杂性。在实际开发中,对于复杂的文本处理需求,可以考虑使用专门的解析库。同时要注意正则表达式的性能,避免使用回溯过多的复杂模式。