正则表达式进阶指南:常用模式与性能优化

正则表达式进阶指南:常用模式与性能优化

正则表达式是文本处理的强大工具,但编写高效的正则表达式需要技巧。本文介绍常用的正则表达式模式,包括邮箱验证、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:将正则表达式可视化
  • 土豆丝工具:在线正则表达式测试工具

六、实践建议

  • 保持简单:不要试图用一个正则表达式解决所有问题
  • 测试充分:测试各种边界情况
  • 文档说明:复杂的正则表达式需要注释说明
  • 性能测试:对于复杂模式,进行性能测试
  • 使用工具:借助在线工具调试和优化正则表达式

💡 提示:正则表达式是强大但复杂的工具。对于非常复杂的文本处理任务,考虑使用解析器或专用库,而不是试图用单个正则表达式解决。

七、总结

正则表达式是开发者必备的技能之一。掌握常用模式和性能优化技巧,可以大大提升文本处理的效率和可靠性。

记住,正则表达式不是万能的。在适当的场景使用适当的工具,才能写出高效、可维护的代码。