正则表达式是文本处理的强大工具,但编写高效的正则表达式需要技巧。本文介绍常用的正则表达式模式,包括邮箱验证、URL 匹配、HTML 标签提取等,同时分享正则表达式性能优化的技巧和常见陷阱。
一、正则表达式基础语法
| 语法 | 说明 | 示例 |
|---|---|---|
. |
匹配任意字符 | a.c 匹配 abc |
\d |
匹配数字 | \d+ 匹配 123 |
\w |
匹配字母数字下划线 | \w+ 匹配 abc_123 |
\s |
匹配空白字符 | \s+ 匹配 空格/制表符 |
^ |
匹配开头 | ^hello 匹配开头的 hello |
$ |
匹配结尾 | world$ 匹配结尾的 world |
* |
匹配 0 次或多次 | ab*c 匹配 ac, abc, abbc |
+ |
匹配 1 次或多次 | ab+c 匹配 abc, abbc |
? |
匹配 0 次或 1 次 | colou?r 匹配 color, colour |
{n} |
精确匹配 n 次 | \d{4} 匹配 1234 |
{n,m} |
匹配 n 到 m 次 | \d{2,4} 匹配 12, 123, 1234 |
[abc] |
字符集,匹配其中之一 | [aeiou] 匹配元音 |
[^abc] |
否定字符集 | [^0-9] 匹配非数字 |
(abc) |
捕获组 | (\d+) 捕获数字 |
(?:abc) |
非捕获组 | (?:ab)+ 不捕获 |
a|b |
或 | cat|dog 匹配 cat 或 dog |
(?=abc) |
正向先行断言 | a(?=b) 匹配后面是 b 的 a |
(?!abc) |
负向先行断言 | a(?!b) 匹配后面不是 b 的 a |
二、常用正则表达式模式
2.1 邮箱验证
// Basic email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Strict email validation
const strictEmailRegex = /^[a-zA-Z0-9](?:[a-zA-Z0-9._%+-]{0,61}[a-zA-Z0-9])?@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z]{2,})+$/;
// Usage example
function validateEmail(email) {
return strictEmailRegex.test(email);
}
console.log(validateEmail('test@example.com')); // true
console.log(validateEmail('invalid@')); // false
2.2 URL 匹配
// Match HTTP/HTTPS URL
const urlRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
// Extract URL components
const urlPattern = /^(https?:\/\/)?([^\/]+)(\/[^?]*)?(\?.*)?$/;
function parseUrl(url) {
const match = url.match(urlPattern);
if (!match) return null;
return {
protocol: match[1] || '',
domain: match[2] || '',
path: match[3] || '',
query: match[4] || ''
};
}
console.log(parseUrl('https://example.com/path?q=1'));
// { protocol: 'https://', domain: 'example.com', path: '/path', query: '?q=1' }
2.3 手机号码验证
// China mainland phone number
const phoneRegex = /^1[3-9]\d{9}$/;
// Support international area code
const internationalPhoneRegex = /^\+?[1-9]\d{1,14}$/;
function validatePhone(phone) {
return phoneRegex.test(phone);
}
console.log(validatePhone('13812345678')); // true
console.log(validatePhone('1234567890')); // false
2.4 IP 地址验证
// IPv4 address validation
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]?)$/;
// IPv6 address validation (simplified)
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
// Support both IPv4 and IPv6
const ipRegex = new RegExp(`(${ipv4Regex.source})|(${ipv6Regex.source})`);
function validateIP(ip) {
return ipRegex.test(ip);
}
console.log(validateIP('192.168.1.1')); // true
console.log(validateIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334')); // true
2.5 日期格式验证
// YYYY-MM-DD format
const dateRegex = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
// Format with time YYYY-MM-DD HH:MM:SS
const datetimeRegex = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]) (?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/;
function validateDate(dateStr) {
if (!dateRegex.test(dateStr)) return false;
// Validate date validity (e.g., Feb 30th)
const date = new Date(dateStr);
return date.toISOString().slice(0, 10) === dateStr;
}
console.log(validateDate('2026-07-09')); // true
console.log(validateDate('2026-02-30')); // false
2.6 HTML 标签提取
// Extract all HTML tags
const tagRegex = /<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>([^<]*)<\/\1>/g;
function extractTags(html) {
const tags = [];
let match;
while ((match = tagRegex.exec(html)) !== null) {
tags.push({
tag: match[1],
content: match[2]
});
}
return tags;
}
const html = '<div>Hello</div><p>World</p>';
console.log(extractTags(html));
// [{ tag: 'div', content: 'Hello' }, { tag: 'p', content: 'World' }]
2.7 密码强度验证
// At least 8 chars, include uppercase, lowercase and numbers
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
// More complex password validation (include special chars)
const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
function checkPasswordStrength(password) {
if (password.length < 8) return 'weak';
if (/^[a-zA-Z]+$/.test(password)) return 'weak';
if (/^[a-zA-Z\d]+$/.test(password)) return 'medium';
if (/^[a-zA-Z\d@$!%*?&]+$/.test(password)) return 'strong';
return 'unknown';
}
console.log(checkPasswordStrength('Password123!')); // strong
三、正则表达式性能优化
3.1 避免贪婪匹配
使用 ? 使量词变为非贪婪模式:
// Greedy matching (may match too much)
const greedyRegex = /.*<\/div>/;
// Lazy matching (recommended)
const lazyRegex = /.*?<\/div>/;
// Example text
const text = '<div>first</div><div>second</div>';
console.log(text.match(greedyRegex)); // matches entire string
console.log(text.match(lazyRegex)); // matches only first div
3.2 使用具体字符集代替通用字符
// Not recommended: using .* to match any character
const badRegex = /user_.*_data/;
// Recommended: using specific character set
const goodRegex = /user_[a-zA-Z0-9_]+_data/;
3.3 使用锚点限制匹配范围
// Not recommended: no anchors, may match middle part
const badRegex = /test/;
// Recommended: using anchors
const startRegex = /^test/; // match at start
const endRegex = /test$/; // match at end
const fullRegex = /^test$/; // exact match
3.4 避免嵌套量词
⚠️ 警告:嵌套量词可能导致灾难性回溯,严重影响性能。
// Dangerous: nested quantifiers
const dangerousRegex = /(a+)+b/;
// Test: this string will cause browser hang!
const testStr = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
// Recommended: use more precise pattern
const safeRegex = /a+b/;
3.5 使用非捕获组
// Not recommended: using capturing group when not needed
const badRegex = /(ab)+/;
// Recommended: using non-capturing group
const goodRegex = /(?:ab)+/;
3.6 预编译正则表达式
// Not recommended: recompiled on each call
function validateEmailBad(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Recommended: precompile regex
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function validateEmailGood(email) {
return emailRegex.test(email);
}
3.7 使用字符类代替交替
// Not recommended: using alternation
const badRegex = /(cat|dog|bird)/;
// Recommended: using character class (if possible)
const goodRegex = /[cdb]at|dog|bird/;
// Better: refactor logic
const betterRegex = /(?:cat|dog|bird)/;
四、常见陷阱
4.1 特殊字符转义
在正则表达式中,以下字符需要转义:. ^ $ * + ? ( ) [ ] { } | \
// Wrong: dot matches any character
const badRegex = /file.txt/;
// Correct: escape the dot
const goodRegex = /file\.txt/;
4.2 多行模式
const text = 'line1\nline2\nline3';
// Default mode: ^ and $ match string start and end
const defaultRegex = /^line/;
console.log(text.match(defaultRegex)); // ['line1']
// Multiline mode: ^ and $ match line start and end
const multilineRegex = /^line/gm;
console.log(text.match(multilineRegex)); // ['line1', 'line2', 'line3']
4.3 Unicode 匹配
// Default: does not match Unicode characters
const defaultRegex = /\w+/;
console.log('Hello'.match(defaultRegex)); // ['Hello']
// Using u flag: matches Unicode
const unicodeRegex = /\w+/u;
console.log('Hello'.match(unicodeRegex)); // ['Hello']
五、正则表达式测试工具
- Regex101:在线测试工具,支持多语言
- RegExr:交互式正则表达式学习工具
- Regexper:将正则表达式可视化
- 土豆丝工具:在线正则表达式测试工具
六、实践建议
- 保持简单:不要试图用一个正则表达式解决所有问题
- 测试充分:测试各种边界情况
- 文档说明:复杂的正则表达式需要注释说明
- 性能测试:对于复杂模式,进行性能测试
- 使用工具:借助在线工具调试和优化正则表达式
💡 提示:正则表达式是强大但复杂的工具。对于非常复杂的文本处理任务,考虑使用解析器或专用库,而不是试图用单个正则表达式解决。
七、总结
正则表达式是开发者必备的技能之一。掌握常用模式和性能优化技巧,可以大大提升文本处理的效率和可靠性。
记住,正则表达式不是万能的。在适当的场景使用适当的工具,才能写出高效、可维护的代码。