正则表达式的应用
一、正则表达式基础
1. 创建正则表达式
在JavaScript中,有两种方式创建正则表达式:
// 字面量形式
const regex1 = /pattern/flags;
// 构造函数形式
const regex2 = new RegExp('pattern', 'flags');
2. 常用修饰符(flags)
i
: 不区分大小写匹配g
: 全局匹配(查找所有匹配而非在第一个匹配后停止)m
: 多行匹配u
: 使用Unicode码点进行匹配s
: 允许.
匹配换行符(ES2018新增)
3. 正则表达式方法
3.1 正则对象的方法
const regex = /hello/;
// test() - 测试是否匹配,返回布尔值
regex.test('hello world'); // true
// exec() - 执行搜索匹配,返回结果数组或null
regex.exec('hello world'); // ["hello", index: 0, input: "hello world", groups: undefined]
3.2 字符串的方法
const str = 'hello world';
// match() - 返回匹配结果
str.match(/hello/); // ["hello", index: 0, input: "hello world", groups: undefined]
// search() - 返回匹配到的位置索引
str.search(/world/); // 6
// replace() - 替换匹配的子串
str.replace(/world/, 'JavaScript'); // "hello JavaScript"
// split() - 使用正则分割字符串
str.split(/\s+/); // ["hello", "world"]
二、正则表达式语法
1. 字符类
\d
: 数字(0-9)\D
: 非数字\w
: 单词字符(字母、数字、下划线)\W
: 非单词字符\s
: 空白字符(空格、制表符、换行符等)\S
: 非空白字符.
: 除换行符外的任意字符(使用s
修饰符时可包含换行符)
2. 量词
*
: 0次或多次+
: 1次或多次?
: 0次或1次{n}
: 恰好n次{n,}
: 至少n次{n,m}
: n到m次
3. 边界匹配
^
: 字符串开头(多行模式下匹配行开头)$
: 字符串结尾(多行模式下匹配行结尾)\b
: 单词边界\B
: 非单词边界
4. 分组与捕获
(...)
: 捕获分组(?:...)
: 非捕获分组(?<name>...)
: 命名捕获组(ES2018)
5. 断言
x(?=y)
: 正向肯定查找(后面是y的x)x(?!y)
: 正向否定查找(后面不是y的x)(?<=y)x
: 反向肯定查找(前面是y的x)(ES2018)(?<!y)x
: 反向否定查找(前面不是y的x)(ES2018)
三、常见场景正则表达式示例
1. 邮箱验证
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// 测试
emailRegex.test('user@example.com'); // true
emailRegex.test('user.name+tag@example.co.uk'); // true
emailRegex.test('invalid.email@'); // false
2. 手机号验证(中国大陆)
const mobileRegex = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/;
// 测试
mobileRegex.test('13800138000'); // true
mobileRegex.test('+8613800138000'); // true
mobileRegex.test('008613800138000'); // true
mobileRegex.test('12345678901'); // false
3. 身份证号验证(中国大陆)
const idCardRegex = /^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/;
// 测试
idCardRegex.test('110105199003077274'); // true
idCardRegex.test('11010519900307727X'); // true
idCardRegex.test('123456199001011234'); // false
4. URL验证
const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([\/\w .-]*)*\/?$/;
// 测试
urlRegex.test('https://www.example.com/path/to/page'); // true
urlRegex.test('http://example.com'); // true
urlRegex.test('www.example.com'); // false
5. 密码强度验证
// 至少8位,包含大小写字母和数字
const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
// 测试
strongPasswordRegex.test('Password1'); // true
strongPasswordRegex.test('password'); // false
strongPasswordRegex.test('PASSWORD1'); // false
strongPasswordRegex.test('Pass1'); // false (长度不足)
6. 日期格式验证(YYYY-MM-DD)
const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// 测试
dateRegex.test('2023-05-15'); // true
dateRegex.test('2023-13-01'); // false
dateRegex.test('2023-02-30'); // false
7. 提取HTML标签中的内容
const htmlRegex = /<([a-z][a-z0-9]*)[^>]*>(.*?)<\/\1>/i;
// 测试
const match = '<div class="content">Hello World</div>'.match(htmlRegex);
console.log(match[2]); // "Hello World"
8. 去除字符串首尾空格
const trimRegex = /^\s+|\s+$/g;
// 测试
' hello world '.replace(trimRegex, ''); // "hello world"
9. 匹配中文
const chineseRegex = /[\u4e00-\u9fa5]/;
// 测试
chineseRegex.test('中文'); // true
chineseRegex.test('English'); // false
10. 匹配IPv4地址
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
// 测试
ipv4Regex.test('192.168.1.1'); // true
ipv4Regex.test('256.168.1.1'); // false
11. 匹配金额(保留2位小数)
const moneyRegex = /^\d+(\.\d{1,2})?$/;
// 测试
moneyRegex.test('123.45'); // true
moneyRegex.test('123'); // true
moneyRegex.test('123.456'); // false
12. 匹配HTML注释
const commentRegex = /<!--[\s\S]*?-->/g;
// 测试
'<!-- 这是注释 --><div></div>'.match(commentRegex); // ["<!-- 这是注释 -->"]
13. 匹配Hex颜色值
const colorRegex = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
// 测试
colorRegex.test('#fff'); // true
colorRegex.test('#FFFFFF'); // true
colorRegex.test('#1234567'); // false
14. 匹配Base64编码
const base64Regex = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$/;
// 测试
base64Regex.test('SGVsbG8gV29ybGQ='); // true
base64Regex.test('123456'); // false
15. 匹配信用卡号(通用)
const creditCardRegex = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/;
// 测试
creditCardRegex.test('4111111111111111'); // true (Visa)
creditCardRegex.test('5500000000000004'); // true (MasterCard)
creditCardRegex.test('340000000000009'); // true (American Express)
四、实用技巧与最佳实践
- 使用命名捕获组提高可读性
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = dateRegex.exec('2023-05-15');
console.log(match.groups); // {year: "2023", month: "05", day: "15"}
- 动态创建正则表达式
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const searchTerm = 'hello.world';
const regex = new RegExp(escapeRegExp(searchTerm), 'gi');
- 使用正向/反向断言
// 匹配后面跟着px的数字
const pxRegex = /\d+(?=px)/;
'12px'.match(pxRegex); // ["12"]
// 匹配前面有$的数字
const dollarRegex = /(?<=\$)\d+/;
'Price: $100'.match(dollarRegex); // ["100"]
- 性能优化
- 避免过度使用通配符
.*
,尽量使用更具体的模式 - 将最可能匹配的模式放在前面
- 避免不必要的捕获组,使用非捕获组
(?:...)
- 预编译常用正则表达式(特别是在循环中使用时)
- 调试正则表达式
可以使用在线工具如 regex101.com 或 regexr.com 来调试和测试正则表达式。
五、常见问题与解决方案
1. 贪婪匹配 vs 惰性匹配
默认情况下,量词是贪婪的(尽可能多匹配),可以在量词后加?
变为惰性匹配(尽可能少匹配)。
const greedyRegex = /<.*>/;
const lazyRegex = /<.*?>/;
'<div>content</div>'.match(greedyRegex)[0]; // "<div>content</div>"
'<div>content</div>'.match(lazyRegex)[0]; // "<div>"
2. 多行匹配
使用m
修饰符使^
和$
匹配每行的开头和结尾。
const multiLineRegex = /^line/gm;
const text = `line 1
line 2
line 3`;
text.match(multiLineRegex); // ["line", "line", "line"]
3. Unicode字符匹配
使用u
修饰符正确处理Unicode字符。
/^.$/.test('😊'); // false
/^.$/u.test('😊'); // true
4. 替换中的特殊字符
在replace
方法中使用$&
, $1
, $2
等引用匹配结果。
'John Smith'.replace(/(\w+)\s(\w+)/, '$2, $1'); // "Smith, John"