Rust正则表达式库pcre2-sys的使用:高性能PCRE2绑定与UTF-8模式支持

Rust正则表达式库pcre2-sys的使用:高性能PCRE2绑定与UTF-8模式支持

概述

pcre2-sys是Rust语言对PCRE2(Perl Compatible Regular Expressions 2)库的绑定。PCRE2是一个强大的正则表达式库,提供与Perl兼容的正则表达式功能。

特性

  • 提供PCRE2库的原始绑定
  • 支持PCRE2 10.42版本
  • 默认优先动态链接系统PCRE2库
  • 支持静态链接(通过环境变量控制)
  • 目前仅支持libpcre-8(PCRE2_CODE_UNIT_WIDTH=8)

安装

在项目目录中运行以下Cargo命令:

cargo add pcre2-sys

或者在Cargo.toml中添加:

pcre2-sys = "0.2.9"

构建选项

  • PCRE2_SYS_STATIC=1:强制静态链接
  • PCRE2_SYS_STATIC=0:强制禁用静态链接
  • PCRE2_SYS_DEBUG:在静态构建时强制启用调试符号

使用示例

以下是使用pcre2-sys进行正则表达式匹配的基本示例:

use pcre2_sys::*;

fn main() {
    // 初始化PCRE2编译上下文
    let compile_context = unsafe { pcre2_compile_context_create_8(std::ptr::null_mut()) };
    
    // 定义正则表达式模式
    let pattern = b"Hello, (\\w+)!";
    let pattern_len = pattern.len() as size_t;
    
    // 定义错误缓冲区和偏移量
    let mut error_buffer = [0i8; 256];
    let mut error_offset = 0;
    
    // 编译正则表达式
    let re = unsafe {
        pcre2_compile_8(
            pattern.as_ptr(),
            pattern_len,
            0,
            &mut error_buffer.as_mut_ptr(),
            &mut error_offset,
            compile_context,
        )
    };
    
    // 检查编译是否成功
    if re.is_null() {
        let error_msg = unsafe { std::ffi::CStr::from_ptr(error_buffer.as_ptr()) };
        eprintln!("PCRE2 compilation failed at offset {}: {}", error_offset, error_msg.to_string_lossy());
        return;
    }
    
    // 创建匹配上下文
    let match_context = unsafe { pcre2_match_context_create_8(std::ptr::null_mut()) };
    
    // 准备要匹配的字符串
    let subject = b"Hello, Rust!";
    let subject_len = subject.len() as size_t;
    
    // 准备匹配数据缓冲区
    let mut ovector = [0; 30]; // 足够大的向量来存储匹配结果
    let rc = unsafe {
        pcre2_match_8(
            re,
            subject.as_ptr(),
            subject_len,
            0, // 起始偏移量
            0, // 选项
            ovector.as_mut_ptr(),
            ovector.len() as size_t,
            match_context,
        )
    };
    
    // 检查匹配结果
    if rc > 0 {
        println!("Match succeeded!");
        
        // 提取捕获组
        let start = ovector[2] as usize;
        let end = ovector[3] as usize;
        let matched = &subject[start..end];
        println!("Captured group: {}", String::from_utf8_lossy(matched));
    } else if rc == PCRE2_ERROR_NOMATCH {
        println!("No match found");
    } else {
        println!("Matching error occurred");
    }
    
    // 清理资源
    unsafe {
        pcre2_code_free_8(re);
        pcre2_compile_context_free_8(compile_context);
        pcre2_match_context_free_8(match_context);
    }
}

UTF-8支持示例

以下示例展示如何使用pcre2-sys处理UTF-8编码的字符串:

use pcre2_sys::*;

fn utf8_regex_match() {
    // 编译支持UTF-8的正则表达式
    let pattern = "こんにちは、(\\p{L}+)!".as_bytes();
    let pattern_len = pattern.len() as size_t;
    
    let mut error_buffer = [0i8; 256];
    let mut error_offset = 0;
    
    let re = unsafe {
        pcre2_compile_8(
            pattern.as_ptr(),
            pattern_len,
            PCRE2_UTF, // 启用UTF-8支持
            &mut error_buffer.as_mut_ptr(),
            &mut error_offset,
            std::ptr::null_mut(),
        )
    };
    
    if re.is_null() {
        let error_msg = unsafe { std::ffi::CStr::from_ptr(error_buffer.as_ptr()) };
        eprintln!("Compilation failed: {}", error_msg.to_string_lossy());
        return;
    }
    
    // 准备UTF-8字符串
    let subject = "こんにちは、Rust!".as_bytes();
    let subject_len = subject.len() as size_t;
    
    let mut ovector = [0; 30];
    let rc = unsafe {
        pcre2_match_8(
            re,
            subject.as_ptr(),
            subject_len,
            0,
            0,
            ovector.as_mut_ptr(),
            ovector.len() as size_t,
            std::ptr::null_mut(),
        )
    };
    
    if rc > 0 {
        let start = ovector[2] as usize;
        let end = ovector[3] as usize;
        let matched = &subject[start..end];
        println!("Matched UTF-8 text: {}", String::from_utf8_lossy(matched));
    }
    
    unsafe {
        pcre2_code_free_8(re);
    }
}

完整示例代码

以下是一个结合基本匹配和UTF-8支持的完整示例:

use pcre2_sys::*;

fn main() {
    // 示例1:基本正则匹配
    basic_regex_match();
    
    // 示例2:UTF-8正则匹配
    utf8_regex_match();
}

fn basic_regex_match() {
    println!("=== 基本正则匹配示例 ===");
    
    // 初始化编译上下文
    let compile_context = unsafe { pcre2_compile_context_create_8(std::ptr::null_mut()) };
    
    // 编译正则表达式
    let pattern = b"Hello, (\\w+)!";
    let re = compile_regex(pattern, 0, compile_context);
    
    // 执行匹配
    let subject = b"Hello, Rust!";
    match_regex(re, subject);
    
    // 清理资源
    unsafe {
        pcre2_compile_context_free_8(compile_context);
    }
}

fn utf8_regex_match() {
    println!("\n=== UTF-8正则匹配示例 ===");
    
    // 编译支持UTF-8的正则表达式
    let pattern = "こんにちは、(\\p{L}+)!".as_bytes();
    let re = compile_regex(pattern, PCRE2_UTF, std::ptr::null_mut());
    
    // 执行匹配
    let subject = "こんにちは、Rust!".as_bytes();
    match_regex(re, subject);
}

// 编译正则表达式的辅助函数
fn compile_regex(pattern: &[u8], options: u32, context: *mut pcre2_compile_context_8) -> *mut pcre2_code_8 {
    let pattern_len = pattern.len() as size_t;
    let mut error_buffer = [0i8; 256];
    let mut error_offset = 0;
    
    let re = unsafe {
        pcre2_compile_8(
            pattern.as_ptr(),
            pattern_len,
            options,
            &mut error_buffer.as_mut_ptr(),
            &mut error_offset,
            context,
        )
    };
    
    if re.is_null() {
        let error_msg = unsafe { std::ffi::CStr::from_ptr(error_buffer.as_ptr()) };
        eprintln!("正则表达式编译失败: {}", error_msg.to_string_lossy());
        std::process::exit(1);
    }
    
    re
}

// 执行正则匹配的辅助函数
fn match_regex(re: *mut pcre2_code_8, subject: &[u8]) {
    let subject_len = subject.len() as size_t;
    let mut ovector = [0; 30];
    
    let rc = unsafe {
        pcre2_match_8(
            re,
            subject.as_ptr(),
            subject_len,
            0,
            0,
            ovector.as_mut_ptr(),
            ovector.len() as size_t,
            std::ptr::null_mut(),
        )
    };
    
    match rc {
        n if n > 0 => {
            println!("匹配成功!");
            let start = ovector[2] as usize;
            let end = ovector[3] as usize;
            let matched = &subject[start..end];
            println!("捕获组: {}", String::from_utf8_lossy(matched));
        },
        PCRE2_ERROR_NOMATCH => println!("未找到匹配"),
        _ => println!("匹配过程中发生错误"),
    }
    
    unsafe {
        pcre2_code_free_8(re);
    }
}

注意事项

  • 这是一个-sys绑定库,主要提供PCRE2的原始绑定
  • 使用前应查阅PCRE2官方文档了解详细API
  • 在Windows上使用GNU工具链时需要安装兼容的C编译器(如MinGW-w64)
  • 已测试支持Windows、Linux和macOS平台

许可证

双重许可:MIT或UNLICENSE


1 回复

Rust正则表达式库pcre2-sys的使用:高性能PCRE2绑定与UTF-8模式支持

pcre2-sys 是 Rust 语言对 PCRE2 (Perl Compatible Regular Expressions 2) 库的原始系统绑定,提供了高性能的正则表达式处理能力,特别支持 UTF-8 编码模式。

主要特性

  • 完整的 PCRE2 功能绑定
  • 支持 UTF-8 编码模式
  • 高性能正则表达式匹配
  • 支持 JIT 编译加速
  • 提供 Unicode 属性支持

使用方法

添加依赖

首先在 Cargo.toml 中添加依赖:

[dependencies]
pcre2-sys = "0.2"

基本使用示例

use pcre2_sys::*;

fn main() {
    unsafe {
        // 创建编译上下文
        let mut compile_context: *mut pcre2_compile_context_8 = std::ptr::null_mut();
        compile_context = pcre2_compile_context_create_8(std::ptr::null_mut());
        
        // 正则表达式模式
        let pattern = b"hello\\s+world\x00" as *const u8;
        
        // 错误代码和偏移量
        let mut errorcode = 0;
        let mut erroroffset = 0;
        
        // 编译正则表达式
        let re = pcre2_compile_8(
            pattern,
            PCRE2_ZERO_TERMINATED,
            0,
            &mut errorcode,
            &mut erroroffset,
            compile_context,
        );
        
        if re.is_null() {
            println!("正则表达式编译失败");
            return;
        }
        
        // 创建匹配上下文
        let match_context = pcre2_match_context_create_8(std::ptr::null_mut());
        
        // 准备匹配字符串
        let subject = b"hello     world\x00" as *const u8;
        let subject_length = 13;
        
        // 执行匹配
        let rc = pcre2_match_8(
            re,
            subject,
            subject_length,
            0,
            0,
            match_context,
            std::ptr::null_mut(),
        );
        
        if rc >= 0 {
            println!("匹配成功!");
        } else {
            println!("匹配失败");
        }
        
        // 释放资源
        pcre2_code_free_8(re);
        pcre2_compile_context_free_8(compile_context);
        pcre2_match_context_free_8(match_context);
    }
}

UTF-8 模式使用

use pcre2_sys::*;

fn utf8_example() {
    unsafe {
        let pattern = b"\\p{L}+\x00" as *const u8; // 匹配任何字母字符
        
        let mut errorcode = 0;
        let mut erroroffset = 0;
        
        let re = pcre2_compile_8(
            pattern,
            PCRE2_ZERO_TERMINATED,
            PCRE2_UCP | PCRE2_UTF, // 启用Unicode属性和UTF-8模式
            &mut errorcode,
            &mut erroroffset,
            std::ptr::null_mut(),
        );
        
        if re.is_null() {
            println!("编译失败");
            return;
        }
        
        let subject = "こんにちは世界\x00".as_ptr() as *const u8;
        let rc = pcre2_match_8(
            re,
            subject,
            PCRE2_ZERO_TERMINATED,
            0,
            0,
            std::ptr::null_mut(),
            std::ptr::null_mut(),
        );
        
        if rc >= 0 {
            println!("匹配到UTF-8文本!");
        }
        
        pcre2_code_free_8(re);
    }
}

高级功能

JIT 编译加速

unsafe {
    // ... 编译正则表达式后
    
    let jit_ret = pcre2_jit_compile_8(re, PCRE2_JIT_COMPLETE as u32);
    if jit_ret == 0 {
        println!("JIT编译成功");
    }
    
    // 使用JIT进行匹配
    let rc = pcre2_jit_match_8(
        re,
        subject,
        subject_length,
        0,
        0,
        match_context,
        std::ptr::null_mut(),
    );
    
    // ...
}

捕获组提取

unsafe {
    // ... 匹配成功后
    
    let mut ovector = [0; 30]; // 足够大的空间存储匹配结果
    let rc = pcre2_match_8(
        re,
        subject,
        subject_length,
        0,
        0,
        match_context,
        ovector.as_mut_ptr(),
    );
    
    if rc > 0 {
        for i in 0..rc {
            let start = ovector[2*i];
            let end = ovector[2*i+1];
            let slice = std::slice::from_raw_parts(subject.add(start), end - start);
            println!("捕获组 {}: {:?}", i, slice);
        }
    }
}

注意事项

  1. pcre2-sys 是底层绑定,使用时需要大量 unsafe 代码
  2. 对于大多数应用,可以考虑使用更高层次的封装如 pcre2 crate
  3. 需要确保 PCRE2 库已正确安装在系统中
  4. 处理错误时需要检查返回值和错误代码

性能建议

  • 对于重复使用的正则表达式,应缓存编译后的模式
  • 启用 JIT 编译可以显著提高匹配性能
  • 对于简单模式,考虑使用 Rust 标准库的 regex crate 可能更高效

完整示例代码

下面是一个结合了基本使用、UTF-8模式和JIT编译的完整示例:

use pcre2_sys::*;

fn main() {
    // 示例1: 基本使用
    basic_example();
    
    // 示例2: UTF-8模式
    utf8_example();
    
    // 示例3: JIT编译
    jit_example();
}

fn basic_example() {
    unsafe {
        println!("=== 基本使用示例 ===");
        
        // 初始化编译上下文
        let compile_context = pcre2_compile_context_create_8(std::ptr::null_mut());
        
        // 编译正则表达式
        let pattern = b"\\d{3}-\\d{4}\x00" as *const u8; // 匹配电话号码格式
        let mut errorcode = 0;
        let mut erroroffset = 0;
        
        let re = pcre2_compile_8(
            pattern,
            PCRE2_ZERO_TERMINATED,
            0,
            &mut errorcode,
            &mut erroroffset,
            compile_context,
        );
        
        if re.is_null() {
            println!("正则表达式编译失败,错误码: {}", errorcode);
            pcre2_compile_context_free_8(compile_context);
            return;
        }
        
        // 匹配测试
        let subject = b"电话: 123-4567\x00" as *const u8;
        let rc = pcre2_match_8(
            re,
            subject,
            PCRE2_ZERO_TERMINATED,
            0,
            0,
            std::ptr::null_mut(),
            std::ptr::null_mut(),
        );
        
        if rc >= 0 {
            println!("找到匹配的电话号码!");
        } else {
            println!("未找到匹配");
        }
        
        // 清理资源
        pcre2_code_free_8(re);
        pcre2_compile_context_free_8(compile_context);
    }
}

fn utf8_example() {
    unsafe {
        println!("\n=== UTF-8模式示例 ===");
        
        // 编译支持UTF-8和Unicode属性的正则表达式
        let pattern = b"\\p{Script=Han}+\x00" as *const u8; // 匹配中文字符
        let mut errorcode = 0;
        let mut erroroffset = 0;
        
        let re = pcre2_compile_8(
            pattern,
            PCRE2_ZERO_TERMINATED,
            PCRE2_UCP | PCRE2_UTF,
            &mut errorcode,
            &mut erroroffset,
            std::ptr::null_mut(),
        );
        
        if re.is_null() {
            println!("UTF-8正则表达式编译失败");
            return;
        }
        
        // 测试UTF-8字符串
        let subject = "这是中文文本\x00".as_ptr() as *const u8;
        let rc = pcre2_match_8(
            re,
            subject,
            PCRE2_ZERO_TERMINATED,
            0,
            0,
            std::ptr::null_mut(),
            std::ptr::null_mut(),
        );
        
        if rc >= 0 {
            println!("成功匹配中文字符!");
        }
        
        pcre2_code_free_8(re);
    }
}

fn jit_example() {
    unsafe {
        println!("\n=== JIT编译示例 ===");
        
        // 编译正则表达式
        let pattern = b"(\\d{4})-(\\d{2})-(\\d{2})\x00" as *const u8; // 匹配日期格式
        let mut errorcode = 0;
        let mut erroroffset = 0;
        
        let re = pcre2_compile_8(
            pattern,
            PCRE2_ZERO_TERMINATED,
            0,
            &mut errorcode,
            &mut erroroffset,
            std::ptr::null_mut(),
        );
        
        if re.is_null() {
            println!("正则表达式编译失败");
            return;
        }
        
        // JIT编译
        let jit_ret = pcre2_jit_compile_8(re, PCRE2_JIT_COMPLETE as u32);
        if jit_ret != 0 {
            println!("JIT编译失败");
            pcre2_code_free_8(re);
            return;
        }
        
        // 准备匹配数据
        let subject = b"日期: 2023-04-15\x00" as *const u8;
        let mut ovector = [0; 30];
        
        // 使用JIT进行匹配
        let rc = pcre2_jit_match_8(
            re,
            subject,
            PCRE2_ZERO_TERMINATED,
            0,
            0,
            std::ptr::null_mut(),
            ovector.as_mut_ptr(),
        );
        
        if rc > 0 {
            println!("匹配成功,找到 {} 个捕获组", rc - 1);
            
            // 提取捕获组
            for i in 0..rc {
                let start = ovector[2*i];
                let end = ovector[2*i+1];
                let slice = std::slice::from_raw_parts(subject.add(start), end - start);
                println!("组 {}: {:?}", i, std::str::from_utf8(slice).unwrap());
            }
        }
        
        pcre2_code_free_8(re);
    }
}

这个完整示例展示了:

  1. 基本正则表达式匹配
  2. UTF-8和Unicode属性支持
  3. JIT编译加速
  4. 捕获组提取
  5. 资源管理和错误处理

使用时需要注意所有PCRE2函数调用都在unsafe块中,并且需要手动管理内存。

回到顶部