【正则表达式实战指南:从入门到精通】

本文将系统讲解正则表达式的核心语法和实战技巧,通过大量实例帮助你掌握这一强大的文本处理工具,提升开发效率。

📋 目录


一、正则表达式基础

1.1 创建正则表达式

javascript 复制代码
// 字面量方式(推荐)
const regex1 = /pattern/flags;

// 构造函数方式(动态创建)
const regex2 = new RegExp('pattern', 'flags');

// 示例
const emailRegex = /^\S+@\S+\.\S+$/;
const dynamicRegex = new RegExp(`^${userInput}$`, 'i');

1.2 常用标志(Flags)

标志 说明 示例
g 全局匹配 /a/g 匹配所有a
i 忽略大小写 /a/i 匹配a和A
m 多行模式 ^$匹配每行开头结尾
s dotAll模式 .匹配包括换行符
u Unicode模式 正确处理Unicode
y 粘性匹配 从lastIndex开始匹配
javascript 复制代码
// 标志组合使用
const regex = /hello/gi;  // 全局、忽略大小写

'Hello hello HELLO'.match(/hello/gi);
// ['Hello', 'hello', 'HELLO']

1.3 基本匹配

javascript 复制代码
// 精确匹配
/hello/.test('hello world');  // true

// 字符类
/[abc]/.test('apple');        // true,匹配a、b或c中任一个
/[a-z]/.test('hello');        // true,匹配小写字母
/[A-Z]/.test('Hello');        // true,匹配大写字母
/[0-9]/.test('123');          // true,匹配数字
/[a-zA-Z0-9]/.test('a1');     // true,匹配字母数字

// 否定字符类
/[^abc]/.test('def');         // true,匹配非a、b、c的字符
/[^0-9]/.test('abc');         // true,匹配非数字

二、元字符与量词

2.1 常用元字符

javascript 复制代码
// 字符类简写
\d  // 数字 [0-9]
\D  // 非数字 [^0-9]
\w  // 单词字符 [a-zA-Z0-9_]
\W  // 非单词字符 [^a-zA-Z0-9_]
\s  // 空白字符 [ \t\n\r\f\v]
\S  // 非空白字符
.   // 任意字符(除换行符)

// 示例
/\d{3}/.test('123');          // true
/\w+/.test('hello_123');      // true
/\s/.test('hello world');     // true

2.2 量词

javascript 复制代码
// 基本量词
*     // 0次或多次
+     // 1次或多次
?     // 0次或1次
{n}   // 恰好n次
{n,}  // 至少n次
{n,m} // n到m次

// 示例
/a*/.test('');           // true,0次
/a+/.test('aaa');        // true,1次或多次
/a?/.test('b');          // true,0次或1次
/a{3}/.test('aaa');      // true,恰好3次
/a{2,4}/.test('aaa');    // true,2到4次

2.3 贪婪与非贪婪

javascript 复制代码
// 贪婪模式(默认)- 尽可能多匹配
const greedy = /<.+>/;
'<div>content</div>'.match(greedy);
// ['<div>content</div>']

// 非贪婪模式 - 尽可能少匹配
const lazy = /<.+?>/;
'<div>content</div>'.match(lazy);
// ['<div>']

// 非贪婪量词
*?    // 0次或多次(非贪婪)
+?    // 1次或多次(非贪婪)
??    // 0次或1次(非贪婪)
{n,m}? // n到m次(非贪婪)

2.4 边界匹配

javascript 复制代码
^     // 字符串开头(多行模式下匹配行开头)
$     // 字符串结尾(多行模式下匹配行结尾)
\b    // 单词边界
\B    // 非单词边界

// 示例
/^hello/.test('hello world');   // true
/world$/.test('hello world');   // true
/\bword\b/.test('a word here'); // true
/\Bword/.test('password');      // true

三、分组与引用

3.1 捕获分组

javascript 复制代码
// 基本分组
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const match = '2024-01-15'.match(regex);
// match[0] = '2024-01-15' (完整匹配)
// match[1] = '2024' (第一个分组)
// match[2] = '01' (第二个分组)
// match[3] = '15' (第三个分组)

// 命名分组
const namedRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const namedMatch = '2024-01-15'.match(namedRegex);
// namedMatch.groups.year = '2024'
// namedMatch.groups.month = '01'
// namedMatch.groups.day = '15'

3.2 非捕获分组

javascript 复制代码
// 非捕获分组 (?:...)
const regex = /(?:https?|ftp):\/\/(\S+)/;
const match = 'https://example.com'.match(regex);
// match[1] = 'example.com' (只捕获域名)

// 对比捕获分组
const captureRegex = /(https?|ftp):\/\/(\S+)/;
const captureMatch = 'https://example.com'.match(captureRegex);
// captureMatch[1] = 'https'
// captureMatch[2] = 'example.com'

3.3 反向引用

javascript 复制代码
// 引用前面的捕获分组
const regex = /(['"]).*?\1/;  // \1 引用第一个分组

regex.test('"hello"');   // true
regex.test("'hello'");   // true
regex.test('"hello\'');  // false,引号不匹配

// 命名反向引用
const namedRegex = /(?<quote>['"]).*?\k<quote>/;
namedRegex.test('"hello"');  // true

// 替换中使用引用
'hello world'.replace(/(\w+) (\w+)/, '$2 $1');
// 'world hello'

// 命名引用替换
'2024-01-15'.replace(
  /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
  '$<d>/$<m>/$<y>'
);
// '15/01/2024'

3.4 选择分支

javascript 复制代码
// 或运算 |
const regex = /cat|dog|bird/;
regex.test('I have a cat');  // true
regex.test('I have a dog');  // true

// 分组中的选择
const urlRegex = /https?:\/\/(www\.)?example\.(com|org|net)/;
urlRegex.test('https://example.com');      // true
urlRegex.test('https://www.example.org');  // true

四、断言与边界

4.1 先行断言

javascript 复制代码
// 正向先行断言 (?=...)
// 匹配后面是...的位置
const regex = /\d+(?=元)/;
'100元'.match(regex);  // ['100']
'100$'.match(regex);   // null

// 负向先行断言 (?!...)
// 匹配后面不是...的位置
const regex2 = /\d+(?!元)/;
'100$'.match(regex2);  // ['100']
'100元'.match(regex2); // ['10'] (匹配到10,因为0后面是元)

4.2 后行断言

javascript 复制代码
// 正向后行断言 (?<=...)
// 匹配前面是...的位置
const regex = /(?<=\$)\d+/;
'$100'.match(regex);   // ['100']
'€100'.match(regex);   // null

// 负向后行断言 (?<!...)
// 匹配前面不是...的位置
const regex2 = /(?<!\$)\d+/;
'€100'.match(regex2);  // ['100']
'$100'.match(regex2);  // ['00']

4.3 断言实战

javascript 复制代码
// 密码强度验证:至少包含大写、小写、数字
const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
strongPassword.test('Abc12345');  // true
strongPassword.test('abc12345');  // false,缺少大写

// 千分位格式化
const addCommas = (num) => {
  return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
addCommas(1234567);  // '1,234,567'

// 提取不在引号内的内容
const regex = /\w+(?=(?:[^"]*"[^"]*")*[^"]*$)/g;

五、JavaScript正则API

5.1 RegExp方法

javascript 复制代码
// test() - 测试是否匹配
/\d+/.test('abc123');  // true

// exec() - 执行匹配,返回详细信息
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const result = regex.exec('2024-01-15');
// result[0] = '2024-01-15'
// result[1] = '2024'
// result.index = 0
// result.input = '2024-01-15'

// 全局匹配迭代
const globalRegex = /\d+/g;
let match;
while ((match = globalRegex.exec('a1b2c3')) !== null) {
  console.log(match[0], match.index);
}
// '1' 1
// '2' 3
// '3' 5

5.2 String方法

javascript 复制代码
// match() - 匹配
'hello world'.match(/\w+/);   // ['hello']
'hello world'.match(/\w+/g);  // ['hello', 'world']

// matchAll() - 返回迭代器(ES2020)
const matches = 'a1b2c3'.matchAll(/(\w)(\d)/g);
for (const match of matches) {
  console.log(match[0], match[1], match[2]);
}
// 'a1' 'a' '1'
// 'b2' 'b' '2'
// 'c3' 'c' '3'

// search() - 返回首次匹配位置
'hello world'.search(/world/);  // 6
'hello world'.search(/xyz/);    // -1

// replace() - 替换
'hello world'.replace(/world/, 'regex');  // 'hello regex'
'aaa'.replace(/a/g, 'b');                 // 'bbb'

// 替换函数
'hello world'.replace(/\w+/g, (match) => {
  return match.toUpperCase();
});
// 'HELLO WORLD'

// 替换函数参数
'2024-01-15'.replace(
  /(\d{4})-(\d{2})-(\d{2})/,
  (match, year, month, day) => {
    return `${day}/${month}/${year}`;
  }
);
// '15/01/2024'

// replaceAll() - 替换所有(ES2021)
'aaa'.replaceAll('a', 'b');  // 'bbb'

// split() - 分割
'a,b;c|d'.split(/[,;|]/);  // ['a', 'b', 'c', 'd']
'a1b2c3'.split(/\d/);      // ['a', 'b', 'c', '']

5.3 常用技巧

javascript 复制代码
// 转义特殊字符
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// 动态创建正则
function createSearchRegex(keyword) {
  const escaped = escapeRegExp(keyword);
  return new RegExp(escaped, 'gi');
}

// 高亮搜索关键词
function highlightKeyword(text, keyword) {
  const regex = createSearchRegex(keyword);
  return text.replace(regex, '<mark>$&</mark>');
}

highlightKeyword('Hello World', 'world');
// 'Hello <mark>World</mark>'

六、实战案例大全

6.1 表单验证

javascript 复制代码
// 手机号(中国大陆)
const phoneRegex = /^1[3-9]\d{9}$/;

// 邮箱
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

// 身份证号
const idCardRegex = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;

// URL
const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([\/\w .-]*)*\/?$/;

// IP地址
const ipRegex = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;

// 密码强度(8-20位,包含大小写字母和数字)
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,20}$/;

// 中文
const chineseRegex = /^[\u4e00-\u9fa5]+$/;

// 用户名(字母开头,允许字母数字下划线,4-16位)
const usernameRegex = /^[a-zA-Z][a-zA-Z0-9_]{3,15}$/;

// 银行卡号(16-19位数字)
const bankCardRegex = /^\d{16,19}$/;

// 邮政编码
const postalCodeRegex = /^[1-9]\d{5}$/;

6.2 数据提取

javascript 复制代码
// 提取URL中的参数
function getUrlParams(url) {
  const params = {};
  const regex = /[?&]([^=&#]+)=([^&#]*)/g;
  let match;
  
  while ((match = regex.exec(url)) !== null) {
    params[match[1]] = decodeURIComponent(match[2]);
  }
  
  return params;
}

getUrlParams('https://example.com?name=john&age=25');
// { name: 'john', age: '25' }

// 提取HTML标签内容
function extractTagContent(html, tagName) {
  const regex = new RegExp(`<${tagName}[^>]*>([\\s\\S]*?)<\\/${tagName}>`, 'gi');
  const matches = [];
  let match;
  
  while ((match = regex.exec(html)) !== null) {
    matches.push(match[1].trim());
  }
  
  return matches;
}

extractTagContent('<p>Hello</p><p>World</p>', 'p');
// ['Hello', 'World']

// 提取所有链接
function extractLinks(html) {
  const regex = /href=["']([^"']+)["']/gi;
  const links = [];
  let match;
  
  while ((match = regex.exec(html)) !== null) {
    links.push(match[1]);
  }
  
  return links;
}

// 提取日期
function extractDates(text) {
  const regex = /\d{4}[-/年]\d{1,2}[-/月]\d{1,2}日?/g;
  return text.match(regex) || [];
}

extractDates('会议时间:2024-01-15 和 2024/02/20');
// ['2024-01-15', '2024/02/20']

6.3 文本处理

javascript 复制代码
// 去除HTML标签
function stripHtml(html) {
  return html.replace(/<[^>]*>/g, '');
}

stripHtml('<p>Hello <b>World</b></p>');
// 'Hello World'

// 驼峰转下划线
function camelToSnake(str) {
  return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
}

camelToSnake('getUserName');  // 'get_user_name'

// 下划线转驼峰
function snakeToCamel(str) {
  return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}

snakeToCamel('get_user_name');  // 'getUserName'

// 首字母大写
function capitalize(str) {
  return str.replace(/\b\w/g, letter => letter.toUpperCase());
}

capitalize('hello world');  // 'Hello World'

// 压缩空白字符
function compressWhitespace(str) {
  return str.replace(/\s+/g, ' ').trim();
}

compressWhitespace('  hello   world  ');  // 'hello world'

// 格式化数字(千分位)
function formatNumber(num) {
  return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

formatNumber(1234567.89);  // '1,234,567.89'

// 脱敏处理
function maskPhone(phone) {
  return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}

maskPhone('13812345678');  // '138****5678'

function maskEmail(email) {
  return email.replace(/(.{2}).*(@.*)/, '$1***$2');
}

maskEmail('example@gmail.com');  // 'ex***@gmail.com'

function maskIdCard(idCard) {
  return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2');
}

maskIdCard('110101199001011234');  // '110101********1234'

6.4 代码处理

javascript 复制代码
// 提取注释
function extractComments(code) {
  const singleLine = code.match(/\/\/.*$/gm) || [];
  const multiLine = code.match(/\/\*[\s\S]*?\*\//g) || [];
  return [...singleLine, ...multiLine];
}

// 移除注释
function removeComments(code) {
  return code
    .replace(/\/\/.*$/gm, '')           // 单行注释
    .replace(/\/\*[\s\S]*?\*\//g, '');  // 多行注释
}

// 提取函数名
function extractFunctionNames(code) {
  const regex = /function\s+(\w+)|(\w+)\s*=\s*(?:async\s*)?\(|(\w+)\s*:\s*(?:async\s*)?\(/g;
  const names = [];
  let match;
  
  while ((match = regex.exec(code)) !== null) {
    names.push(match[1] || match[2] || match[3]);
  }
  
  return names.filter(Boolean);
}

// 提取import语句
function extractImports(code) {
  const regex = /import\s+(?:{[^}]+}|\w+|\*\s+as\s+\w+)\s+from\s+['"]([^'"]+)['"]/g;
  const imports = [];
  let match;
  
  while ((match = regex.exec(code)) !== null) {
    imports.push(match[1]);
  }
  
  return imports;
}

6.5 日志分析

javascript 复制代码
// 解析Nginx日志
function parseNginxLog(line) {
  const regex = /^(\S+) - - \[([^\]]+)\] "(\w+) ([^"]+)" (\d+) (\d+)/;
  const match = line.match(regex);
  
  if (match) {
    return {
      ip: match[1],
      time: match[2],
      method: match[3],
      url: match[4],
      status: parseInt(match[5]),
      size: parseInt(match[6])
    };
  }
  
  return null;
}

// 提取错误日志
function extractErrors(logs) {
  const regex = /\[ERROR\]\s*(.+)/gi;
  return logs.match(regex) || [];
}

// 统计IP访问次数
function countIpVisits(logs) {
  const ipRegex = /^(\d{1,3}\.){3}\d{1,3}/gm;
  const ips = logs.match(ipRegex) || [];
  
  return ips.reduce((acc, ip) => {
    acc[ip] = (acc[ip] || 0) + 1;
    return acc;
  }, {});
}

📊 正则表达式速查表

语法 说明 示例
. 任意字符 a.c 匹配 abc
\d 数字 \d+ 匹配 123
\w 单词字符 \w+ 匹配 abc_123
\s 空白字符 \s+ 匹配空格
^ 开头 ^hello
$ 结尾 world$
* 0次或多次 a*
+ 1次或多次 a+
? 0次或1次 a?
{n,m} n到m次 a{2,4}
[abc] 字符类 [aeiou]
[^abc] 否定字符类 [^0-9]
(...) 捕获分组 (\d+)
(?:...) 非捕获分组 (?:\d+)
(?=...) 正向先行断言 \d+(?=元)
(?!...) 负向先行断言 \d+(?!元)
` `

💡 总结

正则表达式核心要点:

  1. 掌握基础语法:字符类、量词、边界
  2. 理解分组引用:捕获分组、反向引用
  3. 灵活使用断言:先行断言、后行断言
  4. 熟悉JS API:test、exec、match、replace
  5. 贪婪与非贪婪:理解匹配模式
  6. 实战积累:表单验证、数据提取、文本处理

正则表达式是开发者必备技能,熟练掌握能大幅提升开发效率!


💬 如果这篇文章对你有帮助,欢迎点赞收藏!有问题欢迎在评论区讨论~

相关推荐
Patrick在香港10 小时前
Python正则表达式实战:解析和验证香港身份证、车牌、电话格式
开发语言·python·正则表达式
吴声子夜歌2 天前
正则指引——Ruby
开发语言·正则表达式·ruby
吴声子夜歌2 天前
正则指引——Golang
开发语言·golang·正则表达式
吴声子夜歌4 天前
正则指引——PHP
开发语言·正则表达式·php
吴声子夜歌5 天前
正则指引——断言
正则表达式
吴声子夜歌5 天前
正则指引——匹配原理
java·正则表达式
吴声子夜歌7 天前
正则表达式——环视
开发语言·正则表达式
吴声子夜歌7 天前
正则表达式——边界
正则表达式
营养充电站8 天前
C++23 新特性在 CLion 中的实战体验
macos·docker·jupyter·正则表达式