Rust 正则表达式完全指南
Rust通过 regex
crate提供正则表达式支持。本指南将详细介绍Rust中正则表达式的使用方法、性能优化和最佳实践。
1. 基础知识
1.1 添加依赖
在 Cargo.toml
中添加:
toml
[dependencies]
regex = "1.10.2"
1.2 基本使用
rust
use regex::Regex;
fn main() {
// 创建正则表达式
let re = Regex::new(r"\d+").unwrap();
// 基本匹配
let text = "123 456 789";
// 检查是否匹配
assert!(re.is_match(text));
// 查找第一个匹配
if let Some(mat) = re.find(text) {
println!("Found match: {}", mat.as_str()); // "123"
}
// 查找所有匹配
for mat in re.find_iter(text) {
println!("Match: {}", mat.as_str());
}
}
1.3 编译时正则表达式
使用 lazy_static
优化性能:
rust
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref EMAIL_RE: Regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
static ref PHONE_RE: Regex = Regex::new(r"^1[3-9]\d{9}$").unwrap();
}
fn is_valid_email(email: &str) -> bool {
EMAIL_RE.is_match(email)
}
fn is_valid_phone(phone: &str) -> bool {
PHONE_RE.is_match(phone)
}
2. 正则表达式语法
2.1 字符匹配
rust
use regex::Regex;
fn main() {
let text = "Rust 2021 is awesome! Price: $99.99";
// 匹配数字
let digits = Regex::new(r"\d+").unwrap();
for mat in digits.find_iter(text) {
println!("Number: {}", mat.as_str());
}
// 匹配单词
let words = Regex::new(r"\w+").unwrap();
for mat in words.find_iter(text) {
println!("Word: {}", mat.as_str());
}
// 匹配空白字符
let spaces = Regex::new(r"\s+").unwrap();
let parts: Vec<&str> = spaces.split(text).collect();
println!("Parts: {:?}", parts);
// 自定义字符类
let vowels = Regex::new(r"[aeiou]").unwrap();
for mat in vowels.find_iter(text) {
println!("Vowel: {}", mat.as_str());
}
}
2.2 捕获组
rust
use regex::Regex;
fn main() {
let text = "John Smith, Jane Doe, Bob Johnson";
let re = Regex::new(r"(\w+)\s(\w+)").unwrap();
// 使用命名捕获组
let re_named = Regex::new(r"(?P<first>\w+)\s(?P<last>\w+)").unwrap();
// 基本捕获
for caps in re.captures_iter(text) {
println!("Full name: {}", &caps[0]);
println!("First name: {}", &caps[1]);
println!("Last name: {}", &caps[2]);
}
// 命名捕获
for caps in re_named.captures_iter(text) {
println!(
"First: {}, Last: {}",
&caps["first"],
&caps["last"]
);
}
}
3. 高级特性
3.1 替换操作
rust
use regex::Regex;
fn main() {
let text = "My phone is 123-456-7890";
let re = Regex::new(r"\d{3}-\d{3}-\d{4}").unwrap();
// 简单替换
let result = re.replace(text, "XXX-XXX-XXXX");
println!("{}", result);
// 替换所有匹配
let result = re.replace_all(text, "XXX-XXX-XXXX");
println!("{}", result);
// 使用回调函数替换
let result = re.replace_all(text, |caps: ®ex::Captures| {
format!("PHONE({})", &caps[0])
});
println!("{}", result);
}
3.2 正则表达式集合
rust
use regex::RegexSet;
fn main() {
// 创建正则表达式集合
let set = RegexSet::new(&[
r"\w+@\w+\.\w+", // 邮箱
r"\d{3}-\d{3}-\d{4}", // 电话
r"\d{5}", // 邮编
]).unwrap();
let text = "Contact: john@example.com, 123-456-7890, 12345";
// 检查哪些模式匹配
let matches: Vec<_> = set.matches(text).into_iter().collect();
println!("Matching patterns: {:?}", matches);
}
4. 实用工具
4.1 验证器
rust
use regex::Regex;
use lazy_static::lazy_static;
lazy_static! {
static ref VALIDATORS: Validators = Validators::new();
}
pub struct Validators {
email: Regex,
phone: Regex,
password: Regex,
url: Regex,
}
impl Validators {
fn new() -> Self {
Self {
email: Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap(),
phone: Regex::new(r"^1[3-9]\d{9}$").unwrap(),
password: Regex::new(r"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$").unwrap(),
url: Regex::new(r"^https?://[^\s/$.?#].[^\s]*$").unwrap(),
}
}
pub fn is_valid_email(&self, email: &str) -> bool {
self.email.is_match(email)
}
pub fn is_valid_phone(&self, phone: &str) -> bool {
self.phone.is_match(phone)
}
pub fn is_valid_password(&self, password: &str) -> bool {
self.password.is_match(password)
}
pub fn is_valid_url(&self, url: &str) -> bool {
self.url.is_match(url)
}
}
// 使用示例
fn validate_user_input(email: &str, phone: &str) -> Result<(), String> {
if !VALIDATORS.is_valid_email(email) {
return Err("Invalid email format".to_string());
}
if !VALIDATORS.is_valid_phone(phone) {
return Err("Invalid phone format".to_string());
}
Ok(())
}
4.2 文本处理器
rust
use regex::Regex;
use lazy_static::lazy_static;
lazy_static! {
static ref TEXT_PROCESSOR: TextProcessor = TextProcessor::new();
}
pub struct TextProcessor {
url: Regex,
html_tag: Regex,
whitespace: Regex,
}
impl TextProcessor {
fn new() -> Self {
Self {
url: Regex::new(r"https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+[^\s]*").unwrap(),
html_tag: Regex::new(r"<[^>]+>").unwrap(),
whitespace: Regex::new(r"\s+").unwrap(),
}
}
pub fn extract_urls(text: &str) -> Vec<String> {
TEXT_PROCESSOR
.url
.find_iter(text)
.map(|m| m.as_str().to_string())
.collect()
}
pub fn strip_html_tags(html: &str) -> String {
TEXT_PROCESSOR.html_tag.replace_all(html, "").to_string()
}
pub fn clean_whitespace(text: &str) -> String {
TEXT_PROCESSOR
.whitespace
.replace_all(text.trim(), " ")
.to_string()
}
}
5. 性能优化
5.1 编译优化
rust
use regex::RegexBuilder;
fn create_optimized_regex() -> regex::Regex {
RegexBuilder::new(r"\b\w+\b")
.case_insensitive(true)
.multi_line(true)
.dot_matches_new_line(true)
.build()
.unwrap()
}
5.2 性能考虑
rust
use regex::Regex;
use lazy_static::lazy_static;
// 1. 使用 lazy_static 避免重复编译
lazy_static! {
static ref PATTERN: Regex = Regex::new(r"\d+").unwrap();
}
// 2. 使用精确的模式
fn good_pattern() {
let specific = Regex::new(r"^\d{3}$").unwrap(); // 更好
let general = Regex::new(r"\d+").unwrap(); // 较差
}
// 3. 避免回溯
fn avoid_backtracking() {
let bad = Regex::new(r".*foo.*").unwrap(); // 可能导致回溯
let good = Regex::new(r"[^/]*foo[^/]*").unwrap(); // 更好的选择
}
// 4. 使用非捕获组
fn use_non_capturing_groups() {
let capturing = Regex::new(r"(foo)").unwrap(); // 捕获组
let non_capturing = Regex::new(r"(?:foo)").unwrap(); // 非捕获组
}
6. 错误处理
rust
use regex::Regex;
use std::error::Error;
fn compile_regex(pattern: &str) -> Result<Regex, Box<dyn Error>> {
match Regex::new(pattern) {
Ok(re) => Ok(re),
Err(e) => Err(Box::new(e)),
}
}
fn safe_regex_match(pattern: &str, text: &str) -> Result<bool, Box<dyn Error>> {
let re = compile_regex(pattern)?;
Ok(re.is_match(text))
}
// 使用示例
fn main() -> Result<(), Box<dyn Error>> {
match safe_regex_match(r"\d+", "123") {
Ok(true) => println!("Pattern matched!"),
Ok(false) => println!("Pattern did not match."),
Err(e) => eprintln!("Error: {}", e),
}
Ok(())
}
7. 测试
rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_email_validation() {
assert!(VALIDATORS.is_valid_email("test@example.com"));
assert!(!VALIDATORS.is_valid_email("invalid.email"));
assert!(VALIDATORS.is_valid_email("user@domain.co.uk"));
}
#[test]
fn test_phone_validation() {
assert!(VALIDATORS.is_valid_phone("13812345678"));
assert!(!VALIDATORS.is_valid_phone("12345678"));
assert!(!VALIDATORS.is_valid_phone("23812345678"));
}
#[test]
fn test_text_processing() {
let html = "<p>Hello</p><div>World</div>";
assert_eq!(
TextProcessor::strip_html_tags(html),
"HelloWorld"
);
let text = " multiple spaces here ";
assert_eq!(
TextProcessor::clean_whitespace(text),
"multiple spaces here"
);
}
}
总结
Rust的正则表达式实现具有以下特点:
- 高性能的正则表达式引擎
- 安全的API设计
- 丰富的编译时优化选项
- 完整的Unicode支持
最佳实践:
- 使用
lazy_static
缓存编译后的正则表达式 - 选择合适的正则表达式构建方式
- 注意性能优化和内存使用
- 做好错误处理
- 编写完整的测试用例
注意事项:
- 正则表达式编译有开销,应该重用
- 使用适当的模式避免回溯
- 考虑使用
RegexSet
处理多个模式 - 注意字符串的生命周期
记住:在Rust中使用正则表达式时,要充分利用Rust的类型系统和所有权机制,确保代码既高效又安全。合理使用 lazy_static
和其他优化技术可以显著提高性能。