各语言正则表达式应用
正则表达式是跨语言的工具,但不同编程语言的 API 和特性支持有所不同。本章介绍在主流编程语言中使用正则表达式的方法。
JavaScript
JavaScript 的正则表达式支持通过 RegExp 对象和字符串方法使用。
创建正则表达式
// 字面量语法(推荐)
const regex1 = /abc/gi;
// 构造函数语法
const regex2 = new RegExp("abc", "gi");
// 动态构建
const keyword = "hello";
const regex3 = new RegExp(keyword, "i");
常用方法
const str = "The quick brown fox jumps over the lazy dog";
const regex = /\b\w{5}\b/g; // 匹配 5 个字母的单词
// test() - 测试是否匹配
regex.test(str); // true
// exec() - 执行匹配,返回详细信息
let match;
while ((match = regex.exec(str)) !== null) {
console.log(`找到: ${match[0]}, 位置: ${match.index}`);
}
// 输出: 找到: quick, 位置: 4
// 找到: brown, 位置: 10
// 找到: jumps, 位置: 20
// match() - 返回所有匹配
str.match(/\b\w{5}\b/g); // ["quick", "brown", "jumps"]
// matchAll() - 返回迭代器(ES2020)
for (const match of str.matchAll(/\b\w{5}\b/g)) {
console.log(match[0]);
}
// search() - 返回第一个匹配的位置
str.search(/fox/); // 16
// replace() - 替换
str.replace(/fox/, "cat"); // "The quick brown cat jumps..."
str.replace(/\b\w{5}\b/g, "*****"); // 替换所有 5 字母单词
// replace() 使用函数
str.replace(/\b\w{5}\b/g, (word) => word.toUpperCase());
// "The QUICK BROWN FOX JUMPS over the LAZY dog"
// split() - 分割
"a,b,c".split(/,/); // ["a", "b", "c"]
"a, b, c".split(/,\s*/); // ["a", "b", "c"]
命名分组
const date = "2024-03-15";
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = date.match(regex);
console.log(match.groups.year); // "2024"
console.log(match.groups.month); // "03"
console.log(match.groups.day); // "15"
// 在替换中使用命名分组
"2024-03-15".replace(regex, "$<day>/$<month>/$<year>");
// "15/03/2024"
实际案例
// 1. 表单验证
function validateForm(data) {
const rules = {
email: /^[\w.-]+@[\w.-]+\.\w{2,}$/,
phone: /^1[3-9]\d{9}$/,
password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/
};
const errors = {};
for (const [field, regex] of Object.entries(rules)) {
if (!regex.test(data[field])) {
errors[field] = `${field} 格式不正确`;
}
}
return errors;
}
// 2. 解析 URL 参数
function parseUrlParams(url) {
const params = {};
const regex = /[?&](\w+)=([^&]*)/g;
let match;
while ((match = regex.exec(url)) !== null) {
params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
}
return params;
}
parseUrlParams("https://example.com?name=John&age=30");
// { name: "John", age: "30" }
// 3. 高亮关键词
function highlightKeywords(text, keywords) {
const pattern = new RegExp(`(${keywords.join("|")})`, "gi");
return text.replace(pattern, "<mark>$1</mark>");
}
highlightKeywords("JavaScript is great", ["javascript", "great"]);
// "<mark>JavaScript</mark> is <mark>great</mark>"
Python
Python 的 re 模块提供了完整的正则表达式支持。
基本用法
import re
# 编译模式(推荐)
pattern = re.compile(r'\d{3}-\d{4}-\d{4}')
# 或直接使用模块函数
re.search(r'\d+', 'abc123def')
常用函数
text = "The quick brown fox jumps over the lazy dog"
pattern = re.compile(r'\b\w{5}\b')
# match() - 从开始位置匹配
pattern.match(text) # None(不是以 5 字母单词开始)
# search() - 搜索第一个匹配
match = pattern.search(text)
print(match.group()) # "quick"
print(match.start()) # 4
print(match.end()) # 9
print(match.span()) # (4, 9)
# findall() - 返回所有匹配的列表
pattern.findall(text) # ['quick', 'brown', 'jumps']
# finditer() - 返回迭代器
for match in pattern.finditer(text):
print(f"找到: {match.group()}, 位置: {match.span()}")
# fullmatch() - 完整字符串匹配
re.fullmatch(r'\d{4}', '2024') # 匹配
re.fullmatch(r'\d{4}', '2024-03') # 不匹配
分组和命名分组
# 索引分组
date_pattern = re.compile(r'(\d{4})-(\d{2})-(\d{2})')
match = date_pattern.match('2024-03-15')
print(match.group(0)) # "2024-03-15" - 完整匹配
print(match.group(1)) # "2024" - 第一个分组
print(match.group(2)) # "03"
print(match.group(3)) # "15"
print(match.groups()) # ('2024', '03', '15')
# 命名分组
date_pattern = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})')
match = date_pattern.match('2024-03-15')
print(match.group('year')) # "2024"
print(match.group('month')) # "03"
print(match.group('day')) # "15"
print(match.groupdict()) # {'year': '2024', 'month': '03', 'day': '15'}
替换和分割
# sub() - 替换
text = "The quick brown fox"
result = re.sub(r'\b\w{5}\b', '*****', text)
print(result) # "The ***** ***** fox"
# 使用函数进行替换
def to_uppercase(match):
return match.group().upper()
result = re.sub(r'\b\w{5}\b', to_uppercase, text)
print(result) # "The QUICK BROWN fox"
# subn() - 替换并返回替换次数
result, count = re.subn(r'\b\w{5}\b', '*****', text)
print(result) # "The ***** ***** fox"
print(count) # 2
# split() - 分割
re.split(r',\s*', 'a, b, c') # ['a', 'b', 'c']
re.split(r'\W+', 'Words, words, words.') # ['Words', 'words', 'words', '']
标志
# 忽略大小写
re.search(r'hello', 'HELLO', re.IGNORECASE) # 匹配
# 多行模式
text = "line1\nline2"
re.search(r'^line2$', text, re.MULTILINE) # 匹配
# dotAll 模式(. 匹配换行)
re.search(r'line1.line2', "line1\nline2", re.DOTALL) # 匹配
# 详细模式(忽略空白和注释)
pattern = re.compile(r'''
\d{4} # 年
- # 分隔符
\d{2} # 月
- # 分隔符
\d{2} # 日
''', re.VERBOSE)
实际案例
import re
# 1. 日志解析
log_line = '192.168.1.1 - - [10/Oct/2023:13:55:36 +0800] "GET /index.html HTTP/1.1" 200 1234'
log_pattern = re.compile(r'''
(?P<ip>\S+)\s+ # IP 地址
-\s+-\s+ # 身份标识(忽略)
\[(?P<time>[^\]]+)\]\s+ # 时间
"(?P<method>\S+)\s+ # 请求方法
(?P<path>\S+)\s+ # 请求路径
(?P<protocol>[^"]+)"\s+ # 协议
(?P<status>\d+)\s+ # 状态码
(?P<size>\d+) # 响应大小
''', re.VERBOSE)
match = log_pattern.match(log_line)
if match:
print(match.groupdict())
# 2. 数据提取
def extract_emails(text):
pattern = re.compile(r'[\w.-]+@[\w.-]+\.\w{2,}')
return pattern.findall(text)
# 3. 文本清理
def clean_text(text):
# 去除 HTML 标签
text = re.sub(r'<[^>]+>', '', text)
# 合并多个空白
text = re.sub(r'\s+', ' ', text)
# 去除首尾空白
return text.strip()
# 4. 批量重命名
def batch_rename(files, pattern, replacement):
"""
批量重命名文件
例如: 将 "photo_001.jpg" 重命名为 "image_001.jpg"
"""
renamed = []
regex = re.compile(pattern)
for filename in files:
new_name = regex.sub(replacement, filename)
renamed.append((filename, new_name))
return renamed
# 使用示例
files = ['photo_001.jpg', 'photo_002.jpg', 'photo_003.jpg']
result = batch_rename(files, r'^photo', 'image')
# [('photo_001.jpg', 'image_001.jpg'), ...]
Java
Java 的正则表达式通过 java.util.regex 包提供。
基本用法
import java.util.regex.*;
public class RegexExample {
public static void main(String[] args) {
String text = "The quick brown fox jumps over the lazy dog";
// 创建 Pattern
Pattern pattern = Pattern.compile("\\b\\w{5}\\b");
// 创建 Matcher
Matcher matcher = pattern.matcher(text);
// 查找所有匹配
while (matcher.find()) {
System.out.println("找到: " + matcher.group() +
" 位置: " + matcher.start() + "-" + matcher.end());
}
}
}
常用方法
String text = "The quick brown fox jumps over the lazy dog";
Pattern pattern = Pattern.compile("\\b\\w{5}\\b");
Matcher matcher = pattern.matcher(text);
// matches() - 完整匹配
boolean isMatch = Pattern.matches("\\d{4}", "2024"); // true
// find() - 查找下一个匹配
while (matcher.find()) {
System.out.println(matcher.group());
}
// lookingAt() - 从开始匹配
Pattern.compile("The").matcher(text).lookingAt(); // true
// 获取分组
Pattern datePattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher dateMatcher = datePattern.matcher("2024-03-15");
if (dateMatcher.find()) {
System.out.println(dateMatcher.group(0)); // "2024-03-15"
System.out.println(dateMatcher.group(1)); // "2024"
System.out.println(dateMatcher.group(2)); // "03"
System.out.println(dateMatcher.group(3)); // "15"
}
替换
String text = "The quick brown fox";
// 替换所有
String result = text.replaceAll("\\b\\w{5}\\b", "*****");
System.out.println(result); // "The ***** ***** fox"
// 只替换第一个
String result2 = text.replaceFirst("\\b\\w{5}\\b", "*****");
System.out.println(result2); // "The ***** brown fox"
// 使用 Matcher 进行替换
Pattern pattern = Pattern.compile("\\b(\\w{5})\\b");
Matcher matcher = pattern.matcher(text);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
}
matcher.appendTail(sb);
System.out.println(sb.toString()); // "The QUICK BROWN fox"
实际案例
import java.util.regex.*;
import java.util.*;
public class RegexUtils {
// 1. 验证邮箱
public static boolean isValidEmail(String email) {
String regex = "^[\\w.-]+@[\\w.-]+\\.\\w{2,}$";
return Pattern.matches(regex, email);
}
// 2. 验证手机号
public static boolean isValidPhone(String phone) {
String regex = "^1[3-9]\\d{9}$";
return Pattern.matches(regex, phone);
}
// 3. 提取所有链接
public static List<String> extractUrls(String text) {
List<String> urls = new ArrayList<>();
String regex = "https?://[^\\s]+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
urls.add(matcher.group());
}
return urls;
}
// 4. 解析 CSV 行
public static List<String> parseCsvLine(String line) {
List<String> fields = new ArrayList<>();
String regex = "\"([^\"]*)\"|([^,]+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
String field = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
fields.add(field.trim());
}
return fields;
}
// 5. 敏感信息脱敏
public static String maskSensitiveInfo(String text) {
// 手机号脱敏
text = text.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
// 身份证号脱敏
text = text.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2");
return text;
}
public static void main(String[] args) {
// 测试
System.out.println(isValidEmail("[email protected]")); // true
System.out.println(extractUrls("Visit https://google.com or http://example.com"));
System.out.println(maskSensitiveInfo("联系电话: 13812345678"));
}
}
Go
Go 语言的 regexp 包提供正则表达式支持。
基本用法
package main
import (
"fmt"
"regexp"
)
func main() {
// 创建正则(必须编译成功,否则 panic)
re := regexp.MustCompile(`\b\w{5}\b`)
text := "The quick brown fox jumps over the lazy dog"
// 查找所有匹配
matches := re.FindAllString(text, -1)
fmt.Println(matches) // [quick brown jumps]
}
常用方法
text := "The quick brown fox jumps over the lazy dog"
re := regexp.MustCompile(`\b\w{5}\b`)
// MatchString - 测试是否匹配
matched := re.MatchString(text) // true
// FindString - 返回第一个匹配
first := re.FindString(text) // "quick"
// FindAllString - 返回所有匹配
all := re.FindAllString(text, -1) // ["quick", "brown", "jumps"]
all = re.FindAllString(text, 2) // ["quick", "brown"](最多 2 个)
// FindStringIndex - 返回第一个匹配的位置
loc := re.FindStringIndex(text) // [4, 9]
// FindAllStringIndex - 返回所有匹配的位置
locs := re.FindAllStringIndex(text, -1) // [[4, 9], [10, 15], [20, 25]]
分组
// 使用分组
dateRe := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
match := dateRe.FindStringSubmatch("2024-03-15")
// match[0] = "2024-03-15"
// match[1] = "2024"
// match[2] = "03"
// match[3] = "15"
// 查找所有带分组的匹配
allMatches := dateRe.FindAllStringSubmatch("2024-03-15 and 2024-04-20", -1)
// [ ["2024-03-15", "2024", "03", "15"], ["2024-04-20", "2024", "04", "20"] ]
替换和分割
text := "The quick brown fox"
re := regexp.MustCompile(`\b\w{5}\b`)
// ReplaceAllString - 替换所有
result := re.ReplaceAllString(text, "*****")
// "The ***** ***** fox"
// ReplaceAllStringFunc - 使用函数替换
result = re.ReplaceAllStringFunc(text, func(s string) string {
return strings.ToUpper(s)
})
// "The QUICK BROWN fox"
// Split - 分割
re2 := regexp.MustCompile(`,\s*`)
parts := re2.Split("a, b, c", -1)
// ["a", "b", "c"]
实际案例
package main
import (
"fmt"
"regexp"
"strings"
)
// 验证邮箱
func isValidEmail(email string) bool {
re := regexp.MustCompile(`^[\w.-]+@[\w.-]+\.\w{2,}$`)
return re.MatchString(email)
}
// 验证手机号
func isValidPhone(phone string) bool {
re := regexp.MustCompile(`^1[3-9]\d{9}$`)
return re.MatchString(phone)
}
// 提取所有邮箱
func extractEmails(text string) []string {
re := regexp.MustCompile(`[\w.-]+@[\w.-]+\.\w{2,}`)
return re.FindAllString(text, -1)
}
// 解析 URL 参数
func parseURLParams(url string) map[string]string {
params := make(map[string]string)
re := regexp.MustCompile(`[?&](\w+)=([^&]*)`)
matches := re.FindAllStringSubmatch(url, -1)
for _, match := range matches {
if len(match) == 3 {
params[match[1]] = match[2]
}
}
return params
}
// 驿峰命名转下划线
func toSnakeCase(s string) string {
re := regexp.MustCompile(`([a-z])([A-Z])`)
s = re.ReplaceAllString(s, "${1}_${2}")
return strings.ToLower(s)
}
// 下划线转驿峰命名
func toCamelCase(s string) string {
re := regexp.MustCompile(`_([a-z])`)
return re.ReplaceAllStringFunc(s, func(m string) string {
return strings.ToUpper(m[1:])
})
}
func main() {
// 测试
fmt.Println(isValidEmail("[email protected]")) // true
fmt.Println(extractEmails("联系我们: [email protected] 或 [email protected]"))
fmt.Println(parseURLParams("https://example.com?name=John&age=30"))
fmt.Println(toSnakeCase("helloWorld")) // hello_world
fmt.Println(toCamelCase("hello_world")) // helloWorld
}
各语言特性对比
| 特性 | JavaScript | Python | Java | Go |
|---|---|---|---|---|
| 字面量语法 | /pattern/flags | 不支持 | 不支持 | 原始字符串 |
| 命名分组 | ✓ (ES2018) | ✓ | ✓ (Java 7+) | ✗ |
| 零宽断言 | ✓ (ES2018) | ✓ (Python 3.8+) | ✓ (Java 9+) | ✗ |
| 递归模式 | ✗ | ✓ (regex 模块) | ✗ | ✗ |
| Unicode 属性 | ✓ (ES2018) | ✓ (regex 模块) | ✓ | ✗ |
| 原子组 | ✗ | ✗ | ✓ | ✗ |
| 全局匹配 | g 标志 | findall | find 循环 | FindAll |
| 忽略大小写 | i 标志 | re.I | CASE_INSENSITIVE | (?i) |
选择建议
- JavaScript: 适合前端表单验证、实时文本处理
- Python: 适合数据处理、脚本编写、日志分析
- Java: 适合企业级应用、高并发场景
- Go: 适合高性能服务、简洁语法场景
常见问题
1. 转义问题
// JavaScript: 字符串中需要双层转义
new RegExp("\\d{4}"); // 匹配数字
// Python: 使用原始字符串避免转义
re.compile(r"\d{4}")
// Java: 双层转义
Pattern.compile("\\d{4}")
// Go: 原始字符串
regexp.MustCompile(`\d{4}`)
2. Unicode 处理
// JavaScript: 使用 u 标志
/\p{Han}+/u.test("你好"); // true
// Python: 默认支持 Unicode
re.findall(r"[\u4e00-\u9fff]+", "你好")
// Java: 使用 UNICODE_CHARACTER_CLASS
Pattern.compile("\\p{IsHan}+")
3. 性能考虑
- 对于频繁使用的正则,预先编译模式
- 避免在循环中重复编译
- 对于大文本,考虑使用流式处理
小结
不同编程语言的正则表达式实现在语法和功能上有所差异,但核心概念是相通的:
- 编译: 将正则表达式字符串转换为内部表示
- 匹配: 在目标字符串中查找模式
- 提取: 获取匹配的内容和分组
- 替换: 将匹配的内容替换为新内容
选择适合你项目的语言,熟悉其 API 和特性,就能高效地使用正则表达式解决文本处理问题。