是否有替代的正则表达式语法来避免错误“不支持环视,包括向前查找和向后查找”?

nba*_*ari 9 regex rust

我尝试实现这个正则表达式来检查字符串 ( "username") 的长度是否在 3 到 30 之间,仅包含字母 (az)、数字 (0-9) 和句点 (.)(不连续):

use regex::Regex; // 1.3.5

fn main() {
    Regex::new(r"^(?=.{3,30}$)(?!\.)(?!.*\.$)(?!.*?\.\.)[a-z0-9.]+$").unwrap();
}
Run Code Online (Sandbox Code Playgroud)

当尝试编译正则表达式时,我收到此错误:

use regex::Regex; // 1.3.5

fn main() {
    Regex::new(r"^(?=.{3,30}$)(?!\.)(?!.*\.$)(?!.*?\.\.)[a-z0-9.]+$").unwrap();
}
Run Code Online (Sandbox Code Playgroud)

是否有替代的正则表达式或方法来根据这些要求验证字符串?

我可以按照建议删除长度{3,30}并获取字符串长度,但对于第二部分(?!\.)(?!.*\.$)(?!.*?\.\.)[a-z0-9.]+$(防止连续点)?

Dav*_*own 11

当前的问题是“正则表达式”的含义。 维基百科对此有很好的信息,但一个简单的总结是,常规语言是通过一些简单操作定义的语言,包括文字匹配、交替和 Kleene 星号(匹配零个或多个)。正则表达式库添加了一些功能,这些功能不会扩展这种语言,但使其更易于使用(例如能够说[a-z]而不是(a|b|c|d|e|f...|z))。

然后,Perl 出现了,它实现了对正则表达式的支持。然而,它没有使用常用的 NFA/DFA 实现来实现正则表达式,而是使用回溯来实现它们。这样做有两个后果,一,它允许添加常规语言之外的东西,例如回溯,二,它可能非常非常慢。

许多语言都使用了正则表达式的回溯实现,但最近又出现了从表达式中删除某些使它们难以有效实现的功能的复兴,特别是回溯。Go 已经做到了这一点,Re2 库是它的 C/C++ 实现。而且,正如您所发现的,正则表达式箱也以这种方式工作。优点是它总是在线性时间内匹配。

对于您的特定示例,您尝试匹配的内容确实仍然是常规语言,只是需要以不同的方式表达。让我们从简单的部分开始,匹配字符,但不允许连续的点。不要以这种方式思考它,而是将其视为可能匹配字符之间的点,但字符本身不是选项。换句话说,我们可以匹配:[a-z0-9](\.?[a-z0-9])*。我们首先匹配单个字符。如果您想让它以点开头,您可以删除这部分。然后我们需要出现零次或多次可选点,后跟单个非点字符。\.?如果您想在末尾允许有一个点,您可以附加一个。

第二个要求是 3-30 个字符,这会使这个正则表达式变得相当复杂,因为我们的重复序列是 1 或 2 个字符。相反,我建议除了检查正则表达式之外,仅以编程方式检查长度。您还可以创建第二个正则表达式来检查长度,并检查两者是否匹配(常规语言没有 and 操作)。

您可能还会发现,根据您的匹配方式,您可能必须锚定匹配(^在开头放置 a,$在结尾放置 a)。

完整问题的解决方案

use regex::Regex; // 1.3.5

fn main() {
    let pat = Regex::new(r"^[a-z0-9](\.?[a-z0-9])*$").unwrap();
    let names = &[
        "valid123",
        "va.li.d.12.3",
        ".invalid",
        "invalid.",
        "double..dot",
        "ss",
        "really.long.name.that.is.too.long",
    ];
    for name in names {
        let len = name.len();
        let valid = pat.is_match(name) && len >= 3 && len <= 30;
        println!("{:?}: {:?}", name, valid);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案!一个小注释:“and”(交集)和补码是常规语言上的封闭运算,因此真正的“常规”表达式引擎可以有一个“and”运算符。只是很难有效地实施。(而且推理也往往很棘手。) (2认同)