跳到主要内容

各语言正则表达式应用

正则表达式是跨语言的工具,但不同编程语言的 API 和特性支持有所不同。本章介绍在主流编程语言中使用正则表达式的方法。

跨语言正则表达式概览

虽然不同语言的正则表达式 API 各不相同,但核心概念是相通的。理解这些共同点,可以帮助你快速在不同语言之间切换:

共同的核心概念

概念说明所有语言一致
字符类[abc]\d\w
量词*+?{n,m}
锚点^$\b
分组(...)(?:...)
选择|
转义\ 用于转义特殊字符

主要差异点

差异说明
创建方式JavaScript 有字面量 /pattern/,其他语言通常用字符串
API 风格方法命名和参数顺序不同
标志指定JavaScript 用后缀 gi,Python 用常量 `re.I
特性支持命名分组、零宽断言等高级特性的支持程度不同

学习建议

  1. 先掌握核心语法:字符类、量词、分组是所有语言通用的
  2. 了解目标语言 API:熟悉编译、匹配、替换、分割等常用方法
  3. 注意特性差异:在使用高级特性前,确认目标语言是否支持

下面分别介绍各主流语言的正则表达式用法。

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') # 不匹配

Python 特有转义

import re

# \A 和 \Z - 不受多行模式影响
text = """first line
second line
third line"""

# ^ 和 $ 在多行模式下匹配每行的开头和结尾
re.findall(r'^\w+', text, re.MULTILINE)
# ['first', 'second', 'third']

# \A 和 \Z 始终匹配整个字符串的开头和结尾
re.search(r'\A\w+', text) # 只匹配 'first'
re.search(r'\w+\Z', text) # 只匹配 'line'(最后一行的结尾)

# 对比 $ 和 \Z
re.search(r'line$', 'line\n') # 匹配成功($ 可以匹配换行符之前)
re.search(r'line\Z', 'line\n') # 匹配失败(\Z 只匹配真正的结尾)

# Python 3.14+ 新增 \z(字符串绝对结尾)
# \z 在结尾换行符之后也必须匹配
# re.search(r'line\z', 'line\n') # 匹配失败

分组和命名分组

# 索引分组
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)

Python 3.11+ 新特性

Python 3.11 引入了占有量词和原子组,用于优化性能和防止回溯:

import re

# 占有量词(在量词后加 +)
# 普通贪婪匹配
re.match(r'a+a', 'aaaaa') # 匹配成功(回溯后匹配)

# 占有量词(Python 3.11+)
re.match(r'a++a', 'aaaaa') # None(不回溯)

# 原子组(Python 3.11+)
re.match(r'(?>a+)a', 'aaaaa') # None(原子组阻止回溯)

# 实际应用:优化可能产生回溯灾难的模式
# 危险模式
# dangerous = re.compile(r'(a+)+b') # 可能很慢

# 安全模式(使用占有量词)
# safe = re.compile(r'(a++)b') # 快速失败

条件匹配

Python 支持条件匹配语法 (?(id/name)yes|no)

import re

# 根据分组是否匹配选择不同模式
# 如果有开头的 <,则必须有结尾的 >
pattern = re.compile(r'(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|)')

pattern.match('[email protected]') # 匹配成功
pattern.match('<[email protected]>') # 匹配成功
pattern.match('<[email protected]') # None(引号不配对)

实际案例

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
}

Rust

Rust 的 regex crate 是 Rust 生态中最流行的正则表达式库,具有高性能、内存安全和完整 Unicode 支持的特点。

添加依赖

Cargo.toml 中添加:

[dependencies]
regex = "1"

基本用法

use regex::Regex;

fn main() {
// 编译正则表达式
let re = Regex::new(r"\d{4}").unwrap();

// is_match - 检查是否匹配
assert!(re.is_match("2024"));

// find - 查找第一个匹配
let text = "年份是 2024 年";
if let Some(mat) = re.find(text) {
println!("找到: {}", &text[mat.start()..mat.end()]);
// 输出: 找到: 2024
}

// find_iter - 迭代所有匹配
let text = "2023 年到 2024 年";
for mat in re.find_iter(text) {
println!("匹配: {}", &text[mat.start()..mat.end()]);
}
}

捕获分组

use regex::Regex;

fn main() {
let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
let text = "日期: 2024-03-15";

// captures - 获取捕获分组
if let Some(caps) = re.captures(text) {
println!("完整匹配: {}", &caps[0]); // 2024-03-15
println!("年: {}", &caps[1]); // 2024
println!("月: {}", &caps[2]); // 03
println!("日: {}", &caps[3]); // 15
}

// captures_iter - 迭代所有捕获
let text = "2024-03-15 和 2024-04-20";
for caps in re.captures_iter(text) {
println!("日期: {}-{}-{}", &caps[1], &caps[2], &caps[3]);
}
}

命名分组

use regex::Regex;

fn main() {
// 使用 (?P<name>...) 语法定义命名分组
let re = Regex::new(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})").unwrap();
let text = "2024-03-15";

if let Some(caps) = re.captures(text) {
// 通过名称访问分组
println!("年: {}", &caps["year"]);
println!("月: {}", &caps["month"]);
println!("日: {}", &caps["day"]);

// 也可以通过 name 方法获取
if let Some(year) = caps.name("year") {
println!("年份: {}", year.as_str());
}
}
}

替换

use regex::Regex;

fn main() {
let re = Regex::new(r"\d{4}").unwrap();
let text = "年份 2023 变成 2024";

// replace - 替换第一个匹配
let result = re.replace(text, "YEAR");
println!("{}", result); // 年份 YEAR 变成 2024

// replace_all - 替换所有匹配
let result = re.replace_all(text, "YEAR");
println!("{}", result); // 年份 YEAR 变成 YEAR

// 使用捕获分组进行替换
let re = Regex::new(r"(\w+)@(\w+\.\w+)").unwrap();
let text = "邮箱: [email protected]";
// $1 和 $2 引用分组
let result = re.replace(text, "用户: $1, 域名: $2");
println!("{}", result); // 邮箱: 用户: test, 域名: example.com

// 使用命名分组替换
let re = Regex::new(r"(?P<user>\w+)@(?P<domain>\w+\.\w+)").unwrap();
let result = re.replace(text, "用户: $user, 域名: $domain");

// 使用闭包进行替换
let re = Regex::new(r"\d+").unwrap();
let result = re.replace_all("1 + 2 = 3", |caps: &regex::Captures| {
let num: i32 = caps[0].parse().unwrap();
(num * 2).to_string()
});
println!("{}", result); // 2 + 4 = 6
}

分割

use regex::Regex;

fn main() {
let re = Regex::new(r"\s+").unwrap();
let text = "hello world\t\nrust";

// split - 分割字符串
let parts: Vec<&str> = re.split(text).collect();
println!("{:?}", parts); // ["hello", "world", "rust"]

// splitn - 最多分割 n 次
let parts: Vec<&str> = re.splitn(text, 2).collect();
println!("{:?}", parts); // ["hello", "world\trust"]
}

编译选项

Rust regex 通过 RegexBuilder 提供丰富的编译选项:

use regex::{Regex, RegexBuilder};

fn main() {
// 使用 RegexBuilder 配置选项
let re = RegexBuilder::new(r"hello")
.case_insensitive(true) // 忽略大小写
.multi_line(true) // 多行模式
.dot_matches_new_line(true) // . 匹配换行符
.ignore_whitespace(true) // 忽略空白(支持注释)
.size_limit(10 * (1 << 20)) // 设置大小限制
.build()
.unwrap();

assert!(re.is_match("HELLO"));

// 详细模式示例(支持注释)
let re = RegexBuilder::new(r"""
\d{4} # 年份
- # 分隔符
\d{2} # 月份
- # 分隔符
\d{2} # 日期
""")
.ignore_whitespace(true)
.build()
.unwrap();

assert!(re.is_match("2024-03-15"));
}

Unicode 支持

Rust regex 默认完全支持 Unicode:

use regex::Regex;

fn main() {
// 匹配中文字符
let re = Regex::new(r"[\p{Han}]+").unwrap();
assert!(re.is_match("你好世界"));

// 匹配 Unicode 字母
let re = Regex::new(r"\p{L}+").unwrap();
assert!(re.is_match("Hello世界"));

// 匹配 Unicode 数字(包括阿拉伯数字等)
let re = Regex::new(r"\p{N}+").unwrap();
assert!(re.is_match("123٠١٢")); // 包含阿拉伯数字

// 匹配 Emoji
let re = Regex::new(r"\p{Emoji}+").unwrap();
assert!(re.is_match("😀🎉"));
}

实际案例

use regex::Regex;

// 验证邮箱
fn is_valid_email(email: &str) -> bool {
let re = Regex::new(r"^[\w.-]+@[\w.-]+\.\w{2,}$").unwrap();
re.is_match(email)
}

// 提取所有邮箱
fn extract_emails(text: &str) -> Vec<String> {
let re = Regex::new(r"[\w.-]+@[\w.-]+\.\w{2,}").unwrap();
re.find_iter(text).map(|m| m.as_str().to_string()).collect()
}

// 驼峰转下划线
fn to_snake_case(s: &str) -> String {
let re = Regex::new(r"([a-z])([A-Z])").unwrap();
re.replace_all(s, "${1}_${2}").to_lowercase()
}

// 下划线转驼峰
fn to_camel_case(s: &str) -> String {
let re = Regex::new(r"_([a-z])").unwrap();
re.replace_all(s, |caps: &regex::Captures| {
caps[1].to_uppercase()
}).to_string()
}

// 解析 URL 参数
fn parse_url_params(url: &str) -> std::collections::HashMap<String, String> {
let mut params = std::collections::HashMap::new();
let re = Regex::new(r"[?&](\w+)=([^&]*)").unwrap();

for caps in re.captures_iter(url) {
params.insert(caps[1].to_string(), caps[2].to_string());
}
params
}

fn main() {
println!("{}", is_valid_email("[email protected]")); // true
println!("{:?}", extract_emails("联系: [email protected][email protected]"));
println!("{}", to_snake_case("helloWorld")); // hello_world
println!("{}", to_camel_case("hello_world")); // helloWorld
println!("{:?}", parse_url_params("https://example.com?name=John&age=30"));
}

性能特点

Rust regex crate 的核心设计理念是保证线性时间匹配,这意味着无论输入字符串多复杂,匹配时间都与输入长度成线性关系。它使用有限自动机实现,不会出现回溯灾难问题。

use regex::Regex;

fn main() {
// 这种模式在其他语言可能导致回溯灾难
// 但在 Rust regex 中是安全的,保证线性时间
let re = Regex::new(r"(a+)+b").unwrap();

// 即使输入很长也不会卡住
let text = "a".repeat(100) + "c";
assert!(!re.is_match(&text)); // 快速返回 false
}

C# (.NET)

C# 的 System.Text.RegularExpressions 命名空间提供了功能强大的正则表达式支持,具有编译正则、超时控制、命名分组等丰富特性。

基本用法

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
// 创建正则表达式
Regex regex = new Regex(@"\d{4}");

// IsMatch - 检查是否匹配
bool isMatch = regex.IsMatch("年份是 2024");
Console.WriteLine(isMatch); // True

// Match - 查找第一个匹配
Match match = regex.Match("年份是 2024 年");
if (match.Success)
{
Console.WriteLine($"找到: {match.Value}"); // 找到: 2024
Console.WriteLine($"位置: {match.Index}"); // 位置: 4
}

// Matches - 查找所有匹配
MatchCollection matches = regex.Matches("2023 年到 2024 年");
foreach (Match m in matches)
{
Console.WriteLine($"匹配: {m.Value}");
}
}
}

捕获分组

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
Regex regex = new Regex(@"(\d{4})-(\d{2})-(\d{2})");
string text = "日期: 2024-03-15";

Match match = regex.Match(text);
if (match.Success)
{
Console.WriteLine($"完整匹配: {match.Groups[0].Value}"); // 2024-03-15
Console.WriteLine($"年: {match.Groups[1].Value}"); // 2024
Console.WriteLine($"月: {match.Groups[2].Value}"); // 03
Console.WriteLine($"日: {match.Groups[3].Value}"); // 15

// 遍历所有分组
for (int i = 0; i < match.Groups.Count; i++)
{
Console.WriteLine($"分组 {i}: {match.Groups[i].Value}");
}
}
}
}

命名分组

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
// 使用 (?<name>...) 语法定义命名分组
Regex regex = new Regex(@"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})");
string text = "2024-03-15";

Match match = regex.Match(text);
if (match.Success)
{
// 通过名称访问分组
Console.WriteLine($"年: {match.Groups["year"].Value}");
Console.WriteLine($"月: {match.Groups["month"].Value}");
Console.WriteLine($"日: {match.Groups["day"].Value}");
}

// 获取所有分组名称
string[] groupNames = regex.GetGroupNames();
Console.WriteLine($"分组名称: {string.Join(", ", groupNames)}");
}
}

替换

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
Regex regex = new Regex(@"\d{4}");
string text = "年份 2023 变成 2024";

// Replace - 替换所有匹配
string result = regex.Replace(text, "YEAR");
Console.WriteLine(result); // 年份 YEAR 变成 YEAR

// 只替换前 n 个匹配
result = regex.Replace(text, "YEAR", 1);
Console.WriteLine(result); // 年份 YEAR 变成 2024

// 使用分组引用
Regex emailRegex = new Regex(@"(\w+)@(\w+\.\w+)");
result = emailRegex.Replace("邮箱: [email protected]", "用户: $1, 域名: $2");
Console.WriteLine(result); // 邮箱: 用户: test, 域名: example.com

// 使用命名分组引用
Regex namedRegex = new Regex(@"(?<user>\w+)@(?<domain>\w+\.\w+)");
result = namedRegex.Replace("邮箱: [email protected]", "用户: ${user}, 域名: ${domain}");

// 使用 MatchEvaluator 委托
Regex numRegex = new Regex(@"\d+");
result = numRegex.Replace("1 + 2 = 3", match =>
{
int num = int.Parse(match.Value);
return (num * 2).ToString();
});
Console.WriteLine(result); // 2 + 4 = 6
}
}

分割

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
Regex regex = new Regex(@"\s+");
string text = "hello world rust";

// Split - 分割字符串
string[] parts = regex.Split(text);
Console.WriteLine(string.Join(", ", parts)); // hello, world, rust

// Split 最多分割 n 次
parts = regex.Split(text, 2);
Console.WriteLine(string.Join(", ", parts)); // hello, world rust

// 静态方法
parts = Regex.Split("a,b,c", @",\s*");
Console.WriteLine(string.Join(", ", parts)); // a, b, c
}
}

RegexOptions 选项

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
// 常用选项组合
Regex regex = new Regex(
@"hello",
RegexOptions.IgnoreCase | // 忽略大小写
RegexOptions.Multiline | // 多行模式
RegexOptions.Singleline | // 单行模式(. 匹配换行)
RegexOptions.IgnorePatternWhitespace | // 忽略空白和注释
RegexOptions.Compiled // 编译为 IL 代码(提高性能)
);

// 忽略大小写
regex = new Regex(@"hello", RegexOptions.IgnoreCase);
Console.WriteLine(regex.IsMatch("HELLO")); // True

// 多行模式
regex = new Regex(@"^line", RegexOptions.Multiline);
Console.WriteLine(regex.IsMatch("first\nline")); // True

// 单行模式
regex = new Regex(@"a.b", RegexOptions.Singleline);
Console.WriteLine(regex.IsMatch("a\nb")); // True

// 详细模式(支持注释)
regex = new Regex(@"
\d{4} # 年份
- # 分隔符
\d{2} # 月份
- # 分隔符
\d{2} # 日期
", RegexOptions.IgnorePatternWhitespace);
Console.WriteLine(regex.IsMatch("2024-03-15")); // True
}
}

超时控制

.NET 正则表达式支持设置超时时间,防止恶意输入导致的拒绝服务攻击:

using System;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
try
{
// 设置 1 秒超时
Regex regex = new Regex(
@"(a+)+b",
RegexOptions.None,
TimeSpan.FromSeconds(1)
);

// 如果匹配时间超过 1 秒,抛出 RegexMatchTimeoutException
string input = new string('a', 100) + "c";
bool result = regex.IsMatch(input);
}
catch (RegexMatchTimeoutException ex)
{
Console.WriteLine($"正则匹配超时: {ex.Message}");
}
}
}

编译正则表达式

对于频繁使用的正则表达式,可以编译为程序集以提高性能:

using System;
using System.Reflection;
using System.Text.RegularExpressions;

class Program
{
static void Main()
{
// 使用 Compiled 选项编译为 IL 代码
// 首次使用会有编译开销,但后续匹配更快
Regex regex = new Regex(@"\d{4}", RegexOptions.Compiled);

// 编译到程序集(高级用法)
// Regex.CompileToAssembly 方法可以将正则表达式编译为独立的 DLL
// 这适用于大量复杂正则表达式的场景
}
}

实际案例

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class RegexUtils
{
// 验证邮箱
public static bool IsValidEmail(string email)
{
Regex regex = new Regex(@"^[\w.-]+@[\w.-]+\.\w{2,}$");
return regex.IsMatch(email);
}

// 验证手机号
public static bool IsValidPhone(string phone)
{
Regex regex = new Regex(@"^1[3-9]\d{9}$");
return regex.IsMatch(phone);
}

// 提取所有邮箱
public static List<string> ExtractEmails(string text)
{
Regex regex = new Regex(@"[\w.-]+@[\w.-]+\.\w{2,}");
List<string> emails = new List<string>();

foreach (Match match in regex.Matches(text))
{
emails.Add(match.Value);
}
return emails;
}

// 驼峰转下划线
public static string ToSnakeCase(string s)
{
Regex regex = new Regex(@"([a-z])([A-Z])");
return regex.Replace(s, "$1_$2").ToLower();
}

// 下划线转驼峰
public static string ToCamelCase(string s)
{
Regex regex = new Regex(@"_([a-z])");
return regex.Replace(s, m => m.Groups[1].Value.ToUpper());
}

// 解析 URL 参数
public static Dictionary<string, string> ParseUrlParams(string url)
{
Dictionary<string, string> params = new Dictionary<string, string>();
Regex regex = new Regex(@"[?&](\w+)=([^&]*)");

foreach (Match match in regex.Matches(url))
{
params[match.Groups[1].Value] = match.Groups[2].Value;
}
return params;
}

// 敏感信息脱敏
public static string MaskPhone(string phone)
{
Regex regex = new Regex(@"(\d{3})\d{4}(\d{4})");
return regex.Replace(phone, "$1****$2");
}
}

class Program
{
static void Main()
{
Console.WriteLine(RegexUtils.IsValidEmail("[email protected]")); // True
Console.WriteLine(string.Join(", ", RegexUtils.ExtractEmails("联系: [email protected][email protected]")));
Console.WriteLine(RegexUtils.ToSnakeCase("helloWorld")); // hello_world
Console.WriteLine(RegexUtils.ToCamelCase("hello_world")); // helloWorld
Console.WriteLine(RegexUtils.MaskPhone("13812345678")); // 138****5678
}
}

.NET 7+ 新特性

.NET 7 引入了新的高性能 API:

using System.Text.RegularExpressions;

// 使用 source generator 生成编译时正则表达式
[GeneratedRegex(@"\d{4}")]
private static partial Regex YearRegex();

// 使用
bool isMatch = YearRegex().IsMatch("2024");

// 枚举匹配(更高效)
string text = "2023 年到 2024 年";
foreach (var match in YearRegex().EnumerateMatches(text.AsSpan()))
{
Console.WriteLine(text.Substring(match.Index, match.Length));
}

// 计算匹配数
int count = YearRegex().Count(text);

各语言特性对比

特性JavaScriptPythonJavaGoRustC#
字面量语法/pattern/flags不支持不支持`原始字符串`不支持不支持
命名分组 (ES2018) (Java 7+)
零宽断言 (ES2018) (Java 9+)
后行断言 (ES2018)
递归模式 (regex 模块) (平衡组)
Unicode 属性 (ES2018) (regex 模块)
原子组 (3.11+)
占有量词 (3.11+)
条件匹配
Modifier 语法 (ES2024)
全局匹配g 标志findallfind 循环FindAllfind_iterMatches
忽略大小写i 标志re.ICASE_INSENSITIVE(?i)case_insensitiveIgnoreCase
匹配索引d 标志 (ES2022)Match.span()start(), end()FindStringIndexstart(), end()Index, Length
Unicode Setsv 标志 (ES2024)
字符串属性v 标志 (ES2024)
集合运算v 标志 (ES2024)
字符串字面量 \q{}v 标志 (ES2024)
escape 方法 (ES2025)re.escape() (3.7+)Pattern.quote()regexp.QuoteMetaregex::escape()Regex.Escape()
超时控制
编译优化 (Compiled)
线性时间保证

选择建议

  1. JavaScript: 适合前端表单验证、实时文本处理
  2. Python: 适合数据处理、脚本编写、日志分析
  3. Java: 适合企业级应用、高并发场景
  4. Go: 适合高性能服务、简洁语法场景
  5. Rust: 适合系统编程、需要内存安全和性能保证的场景
  6. C#: 适合 .NET 企业级应用、需要编译优化和超时控制的场景

常见问题

1. 转义问题

// JavaScript: 字符串中需要双层转义
new RegExp("\\d{4}"); // 匹配数字

// Python: 使用原始字符串避免转义
re.compile(r"\d{4}")

// Java: 双层转义
Pattern.compile("\\d{4}")

// Go: 原始字符串
regexp.MustCompile(`\d{4}`)

// Rust: 原始字符串
Regex::new(r"\d{4}").unwrap()

// C#: 使用逐字字符串
new Regex(@"\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}+")

// Go: 使用 Unicode 范围
regexp.MustCompile(`[\p{Han}]+`)

// Rust: 内置 Unicode 支持
Regex::new(r"\p{Han}+").unwrap()

// C#: 使用 Unicode 类别
new Regex(@"\p{IsHan}+")

3. 性能考虑

  • 对于频繁使用的正则,预先编译模式
  • 避免在循环中重复编译
  • 对于大文本,考虑使用流式处理
  • Rust 特有优势: 保证线性时间匹配,不会出现回溯灾难
  • C# 特有特性: 设置超时防止 ReDoS 攻击

小结

不同编程语言的正则表达式实现在语法和功能上有所差异,但核心概念是相通的:

  • 编译: 将正则表达式字符串转换为内部表示
  • 匹配: 在目标字符串中查找模式
  • 提取: 获取匹配的内容和分组
  • 替换: 将匹配的内容替换为新内容

各语言的独特优势:

  • JavaScript: 浏览器原生支持,ES2024 引入强大的 v 标志
  • Python: 灵活的 API,支持条件匹配和原子组
  • Java: 企业级成熟方案,完整特性支持
  • Go: 简洁语法,原生支持良好
  • Rust: 保证线性时间匹配,内存安全,适合系统编程
  • C#: 编译优化、超时控制,.NET 7+ 支持 Source Generator

选择适合你项目的语言,熟悉其 API 和特性,就能高效地使用正则表达式解决文本处理问题。