#regex #regular #expressions #engine #matching #faster #compile

nightly regex_macros

Rust 静态编译正则表达式的实现。除非您特别需要编译时正则表达式或保证不会分配内存的匹配引擎,否则您应该暂时优先使用普通的 regex crate(因为它几乎总是更快)。

39 个版本

使用旧的 Rust 2015

0.2.0 2017年1月1日
0.1.38 2016年7月6日
0.1.37 2016年6月16日
0.1.34 2016年3月28日
0.1.0 2014年12月13日

2217算法

Download history 138/week @ 2024-03-11 105/week @ 2024-03-18 23/week @ 2024-03-25 264/week @ 2024-04-01 53/week @ 2024-04-08 94/week @ 2024-04-15 110/week @ 2024-04-22 82/week @ 2024-04-29 54/week @ 2024-05-06 83/week @ 2024-05-13 157/week @ 2024-05-20 189/week @ 2024-05-27 120/week @ 2024-06-03 108/week @ 2024-06-10 80/week @ 2024-06-17 75/week @ 2024-06-24

每月下载量 431
不到 10 crate 中使用

MIT/Apache 许可

23KB
461

regex

本 crate 提供了在字符串中搜索正则表达式匹配(也称为“正则表达式”)的例程。本 crate 支持的正则表达式语法与其他正则表达式引擎类似,但它缺少一些不知道如何高效实现的特性。这包括但不限于前瞻和后引用。作为交换,本 crate 中所有正则表达式搜索的最坏情况时间复杂度均为 O(m * n),其中 m 与正则表达式的大小成正比,n 与被搜索字符串的大小成正比。

Build status Crates.io

文档

模块文档及示例。模块文档还包括了对所支持语法的全面描述。

Regex 类型 上可以找到各种匹配函数和迭代器的示例文档。

用法

要将此 crate 添加到您的仓库中,请在您的 Cargo.toml 中添加 regex,或运行 cargo add regex

以下是一个简单的示例,匹配 YYYY-MM-DD 格式的日期,并打印年份、月份和日期:

use regex::Regex;

fn main() {
    let re = Regex::new(r"(?x)
(?P<year>\d{4})  # the year
-
(?P<month>\d{2}) # the month
-
(?P<day>\d{2})   # the day
").unwrap();

    let caps = re.captures("2010-03-14").unwrap();
    assert_eq!("2010", &caps["year"]);
    assert_eq!("03", &caps["month"]);
    assert_eq!("14", &caps["day"]);
}

如果您有很多文本中的日期需要迭代,那么可以很容易地通过迭代器适配上述示例:

use regex::Regex;

fn main() {
    let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
    let hay = "On 2010-03-14, foo happened. On 2014-10-14, bar happened.";

    let mut dates = vec![];
    for (_, [year, month, day]) in re.captures_iter(hay).map(|c| c.extract()) {
        dates.push((year, month, day));
    }
    assert_eq!(dates, vec![
      ("2010", "03", "14"),
      ("2014", "10", "14"),
    ]);
}

用法:避免在循环中编译相同的正则表达式

在循环中编译相同的正则表达式是一种反模式,因为编译通常代价很高。编译所需时间从几微秒到几毫秒不等,具体取决于正则表达式的大小。这不仅使编译本身代价高昂,还阻碍了匹配引擎内部重用分配的优化。

在Rust中,如果正则表达式在辅助函数内部使用,传递它们有时可能很痛苦。因此,我们建议使用once_cell库来确保正则表达式只编译一次。例如

use {
    once_cell::sync::Lazy,
    regex::Regex,
};

fn some_helper_function(haystack: &str) -> bool {
    static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"...").unwrap());
    RE.is_match(haystack)
}

fn main() {
    assert!(some_helper_function("abc"));
    assert!(!some_helper_function("ac"));
}

具体来说,在这个示例中,正则表达式将在第一次使用时进行编译。在随后的使用中,它将重用之前的编译。

用法:在&[u8]上匹配正则表达式

此库的主要API(regex::Regex)要求调用者传递一个用于搜索的&str。在Rust中,&str必须是有效的UTF-8,这意味着主要API不能用于搜索任意字节。

要匹配任意字节,请使用regex::bytes::Regex API。该API与主要API相同,不同之处在于它接受一个&[u8]进行搜索,而不是一个&str。这些&[u8] API还允许在正则表达式中禁用Unicode模式,即使模式会匹配无效的UTF-8。例如,(?-u:.)regex::Regex中不允许,但在regex::bytes::Regex中允许,因为(?-u:.)匹配除了\n之外的所有字节。相反,.将匹配除了\n之外的任何Unicode标量值的UTF-8编码。

此示例展示了如何在字节数组切片中找到所有空终止的字符串

use regex::bytes::Regex;

let re = Regex::new(r"(?-u)(?<cstr>[^\x00]+)\x00").unwrap();
let text = b"foo\xFFbar\x00baz\x00";

// Extract all of the strings without the null terminator from each match.
// The unwrap is OK here since a match requires the `cstr` capture to match.
let cstrs: Vec<&[u8]> =
    re.captures_iter(text)
      .map(|c| c.name("cstr").unwrap().as_bytes())
      .collect();
assert_eq!(vec![&b"foo\xFFbar"[..], &b"baz"[..]], cstrs);

注意,这里的[^\x00]+将匹配任何字节,除了NUL,包括像\xFF这样的字节,它们不是有效的UTF-8。当使用主要API时,[^\x00]+将匹配任何有效的UTF-8序列,除了NUL

用法:同时匹配多个正则表达式

本例演示了如何使用RegexSet在搜索文本的单次扫描中匹配多个(可能重叠的)正则表达式

use regex::RegexSet;

let set = RegexSet::new(&[
    r"\w+",
    r"\d+",
    r"\pL+",
    r"foo",
    r"bar",
    r"barfoo",
    r"foobar",
]).unwrap();

// Iterate over and collect all of the matches.
let matches: Vec<_> = set.matches("foobar").into_iter().collect();
assert_eq!(matches, vec![0, 2, 3, 4, 6]);

// You can also test whether a particular regex matched:
let matches = set.matches("foobar");
assert!(!matches.matched(5));
assert!(matches.matched(6));

用法:将正则表达式内部作为库使用

regex-automata 目录》中包含一个crate,该crate公开了《regex》crate所使用的所有内部匹配引擎。其想法是,《regex》crate为99%的使用场景提供了简单的API,而《regex-automata》则公开了大量可定制的功能。

regex-automata》的文档。

用法:正则表达式解析器

该仓库包含一个crate,它提供了一个经过良好测试的正则表达式解析器、抽象语法和高级中间表示,便于分析。它不提供编译或执行的功能。如果您正在实现自己的正则表达式引擎或需要对正则表达式的语法进行分析,这可能很有用。否则,不建议一般使用。

regex-syntax》的文档。

crate功能

该crate附带一些功能,允许用户在二进制大小、编译时间和运行时性能之间进行权衡。该crate的用户可以选择性地禁用Unicode表,或从该crate提供的各种优化中选择禁用。

当所有这些功能都禁用时,运行时匹配性能可能会大大降低,但如果您正在匹配短字符串,或者高性能不是必需的,则这种配置完全可以满足需求。要禁用所有这些功能,请使用以下 Cargo.toml 依赖配置

[dependencies.regex]
version = "1.3"
default-features = false
# Unless you have a specific reason not to, it's good sense to enable standard
# library support. It enables several optimizations and avoids spin locks. It
# also shouldn't meaningfully impact compile times or binary size.
features = ["std"]

这将把 regex 的依赖树缩减到两个crate:regex-syntaxregex-automata

可以禁用的完整功能集在文档的“crate功能”部分中。

性能

该crate的目标之一是让正则表达式引擎“快速”。这是一个有些模糊的目标,通常有两种解释方式。首先,这意味着所有搜索都将在最坏情况下花费 O(m * n) 的时间,其中 mlen(regex) 成正比,而 nlen(haystack) 成正比。其次,它还意味着即使在时间复杂度约束之外,正则表达式搜索在实践中也是“快速”的。

虽然第一种解释非常明确,但第二种解释仍然模糊。尽管模糊,但它指导了这个crate的架构以及它所做出的各种权衡。例如,以下是一些基于“快速”目标的一般性架构声明:

  • 当在更快的正则表达式搜索和更快的 Rust 编译时间 之间做出选择时,该crate通常会优先选择更快的正则表达式搜索。
  • 当在更快的正则表达式搜索和更快的 正则表达式编译时间 之间做出选择时,该crate通常会优先选择更快的正则表达式搜索。也就是说,如果这意味着搜索速度更快,那么 Regex::new 稍微慢一些通常是可接受的。(这是一个需要小心权衡的问题,因为 Regex::new 的速度需要保持合理。这就是为什么应该避免反复重新编译相同的正则表达式的原因。)
  • 在给定的选择中,如果要在更快的正则表达式搜索和更简单的API设计之间进行选择,这个库通常会优先选择更快的正则表达式搜索。例如,如果不在乎性能,我们可以去掉 Regex::is_matchRegex::find API,而是只依赖 Regex::captures

或许还有更多方式,快速会影响到一些事情。

虽然这个存储库曾经提供自己的基准测试套件,但它已经被移动到 rebar。基准测试非常广泛,比rebar的README(仅限于用于比较正则表达式引擎性能的“精选”集)中显示的要多得多。要运行此库的所有基准测试,首先开始通过克隆和安装 rebar

$ git clone https://github.com/BurntSushi/rebar
$ cd rebar
$ cargo install --path ./

然后仅为此库构建基准测试工具

$ rebar build -e '^rust/regex$'

作为测试运行此库的所有基准测试(每个基准测试执行一次以确保它工作)

$ rebar measure -e '^rust/regex$' -t

记录所有基准测试的度量并保存到CSV文件中

$ rebar measure -e '^rust/regex$' | tee results.csv

探索基准测试时间

$ rebar cmp results.csv

查看 rebar 文档以获取更多有关其工作方式和如何与其他正则表达式引擎比较结果的信息。

黑客攻击

regex 库基本上是对 meta::Regex 的薄包装,该库来自 regex-automata。因此,如果您想研究此库的内部结构,您可能希望查看 regex-syntax(用于解析)或 regex-automata(用于有限自动机的构建和搜索例程)。

我关于正则表达式内部结构的 博客 深入探讨了。

最小Rust版本策略

此库支持的最小 rustc 版本是 1.65.0

策略是,使用此库所需的最低Rust版本可以在次要版本更新中增加。例如,如果regex 1.0需要Rust 1.20.0,则regex 1.0.z的所有值也将需要Rust 1.20.0或更高版本。但是,regex 1.y对于 y > 0 可能需要更高的最低Rust版本。

许可证

此项目根据您的选择许可

任选其一。

regex-syntax/src/unicode_tables/ 中的数据根据Unicode许可协议许可 (LICENSE-UNICODE)。

依赖关系