正则表达式
一、正则表达式基础
正则表达式(RegExp)是用于匹配字符串模式的工具,在 JavaScript 中可通过以下两种方式创建:
// 字面量形式(推荐)
const pattern = /abc/i;
// 构造函数形式
const pattern = new RegExp('abc', 'i');
标志(Flags):
i
:忽略大小写g
:全局匹配(找到所有匹配,而非第一个)m
:多行模式(^
和$
匹配行首/行尾)s
:允许.
匹配换行符u
:Unicode 模式(处理大于\uFFFF
的字符)y
:粘性匹配(从lastIndex
位置开始匹配)
二、元字符与特殊符号
字符 | 描述 | 示例 |
---|---|---|
. | 匹配除换行符外的任意字符 | /a.b/ 匹配 acb , aab |
\d | 匹配数字(等价于 [0-9] ) | /\d{3}/ 匹配 123 |
\D | 匹配非数字(等价于 [^0-9] ) | /\D+/ 匹配 abc |
\w | 匹配单词字符(字母、数字、下划线) | /\w{5}/ 匹配 hello |
\W | 匹配非单词字符 | /\W/ 匹配空格、标点 |
\s | 匹配空白字符(空格、制表符、换行符) | /\s+/ 匹配多个空格 |
\S | 匹配非空白字符 | /\S/ 匹配 a , 1 , $ |
^ | 匹配字符串开头(多行模式下匹配行首) | /^hello/ 匹配 hello world |
$ | 匹配字符串结尾(多行模式下匹配行尾) | /world$/ 匹配 hello world |
[] | 匹配方括号内的任意字符 | /[aeiou]/ 匹配元音字母 |
[^] | 匹配不在方括号内的任意字符 | /[^0-9]/ 匹配非数字 |
| | 或操作 | /cat|dog/ 匹配 cat 或 dog |
三、量词
量词 | 描述 | 示例 |
---|---|---|
* | 匹配前一个元素 0 次或多次 | /a*/ 匹配 ``, a , aa |
+ | 匹配前一个元素 1 次或多次 | /a+/ 匹配 a , aa |
? | 匹配前一个元素 0 次或 1 次(可选) | /colou?r/ 匹配 color 或 colour |
{n} | 精确匹配 n 次 | /a{3}/ 匹配 aaa |
{n,} | 至少匹配 n 次 | /a{2,}/ 匹配 aa , aaa |
{n,m} | 匹配 n 到 m 次(包含 n 和 m) | /a{2,4}/ 匹配 aa , aaa , aaaa |
四、贪婪与非贪婪匹配
-
贪婪匹配:默认行为,尽可能多地匹配。
'aabab'.match(/a.*b/); // 返回 ['aabab']
-
非贪婪匹配:通过
?
启用,尽可能少地匹配。'aabab'.match(/a.*?b/); // 返回 ['aab']
五、分组与捕获
语法 | 描述 | 示例 |
---|---|---|
(...) | 捕获组,提取匹配的子串 | /(\d{2})-(\d{2})/ 提取日期 |
(?:...) | 非捕获组,仅用于分组 | /a(?:b|c)/ 匹配 ab 或 ac |
\1 , \2 | 反向引用,引用前面的捕获组 | /(\w+) \1/ 匹配 hello hello |
六、断言(零宽断言)
语法 | 描述 | 示例 |
---|---|---|
(?=...) | 正向先行断言(后面必须匹配) | /hello(?= world)/ 匹配 hello 后跟 world |
(?!...) | 负向先行断言(后面不能匹配) | /hello(?! world)/ 匹配 hello 但后面不能是 world |
(?<=...) | 正向后行断言(前面必须匹配) | /(?<=\$)\d+/ 匹配 $ 后的数字 |
(?<!...) | 负向后行断言(前面不能匹配) | /(?<!\$)\d+/ 匹配前面不是 $ 的数字 |
七、JavaScript 中的 RegExp 方法
方法 | 描述 | 示例 |
---|---|---|
test() | 检查字符串是否匹配,返回布尔值 | /abc/.test('abc') // true |
exec() | 执行匹配,返回数组或 null | /\d+/.exec('a123b') // ['123'] |
match() | 字符串方法,返回匹配结果 | 'a1b2'.match(/\d/g) // ['1', '2'] |
matchAll() | 返回所有匹配的迭代器 | 'a1b2'.matchAll(/\d/g) |
replace() | 替换匹配的子串 | 'hello'.replace(/h/, 'H') // 'Hello' |
search() | 返回第一个匹配的索引 | 'abc'.search(/b/) // 1 |
split() | 按匹配的模式分割字符串 | 'a,b,c'.split(/,/) // ['a', 'b', 'c'] |
八、实用案例
1. 验证邮箱
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
emailRegex.test('test@example.com'); // true
2. 提取 URL 参数
const url = 'https://example.com?name=john&age=30';
const params = {};
url.replace(/[?&]([^=]+)=([^&]+)/g, (_, key, value) => {
params[key] = value;
});
// params: { name: 'john', age: '30' }
3. 替换 HTML 标签
const html = '<p>Hello <b>World</b></p>';
const text = html.replace(/<[^>]+>/g, '');
// text: 'Hello World'
4. 格式化电话号码
const phone = '1234567890';
const formatted = phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
// formatted: '(123) 456-7890'
九、性能优化
-
预编译正则表达式:
// 避免在循环中重复创建相同的正则表达式
const regex = /pattern/g;
for (const str of array) {
str.match(regex);
} -
简化复杂模式:
- 使用非捕获组
(?:...)
替代捕获组,减少内存开销。 - 避免过度使用回溯(如嵌套量词)。
- 使用非捕获组
十、常见陷阱
-
点号不匹配换行符:
'a\nb'.match(/a.b/); // null
'a\nb'.match(/a.b/s); // ['a\nb'](使用 s 标志) -
全局标志与 lastIndex:
const regex = /a/g;
regex.exec('ab'); // ['a']
regex.exec('ab'); // null(lastIndex 已移动)
regex.lastIndex = 0; // 重置 lastIndex -
Unicode 字符处理:
'😂'.match(/./); // ['�'](错误)
'😂'.match(/./u); // ['😂'](使用 u 标志)
十一、总结
正则表达式是 JavaScript 中强大的字符串处理工具,掌握其核心概念(元字符、量词、分组、断言)和方法(test
、exec
、match
、replace
),可以高效解决各种文本处理需求。在实际应用中,建议使用工具(如 Regex101)辅助调试,并注意性能优化和边缘情况处理。