正则表达式:一个可选的子字符串,可以出现在两个地方之一,但不能同时出现在两个地方



我正在验证一个带有正则表达式、PCRE 风格的字符串。我有一个子字符串,可以选择出现在两个可能的地方之一 - 但不能同时出现。我如何为此编写正则表达式?

没有子字符串的正则表达式是

M[01]([ ]*(?[A-Z]{3})?)?

子字符串具有正则表达式 C[0-5],可以在括号之前或之后出现,也可以根本不存在。它可以用空格分隔,也可以不用空格分隔。

有效示例(所有示例都包含空格以提高可读性,但没有空格的示例也有效(:

M1
M1 C1
M1 (OSS)
M1 C1 (OSS)
M1 (OSS) C1

无效示例:

M1 C1 (OSS) C1

我想出的最接近的东西是

M[01]([ ]*C[1-5]?)([ ]*(?[A-Z]{3})?)?([ ]*C[1-5]?)

但这也会接受无效示例。由于我只有两个位置,我当然可以列举不同的组合,但我不喜欢这种解决方案,因为它不能很好地扩展到更多可能的位置。

如果这很重要,这是一个将存在于更长字符串中进行验证的组,因此正则表达式将作为子例程嵌入到较大的字符串中。

一种选择是,当第一个 C 部分(可能(匹配时,捕获捕获组中的 C。然后,在可能的 C 部分的第二个位置,在匹配第一个捕获组之前对其进行负展望:

^M[01](?: *(C)[1-5])? *(?:(?[A-Z]{3})?(?: *(?!1)C[1-5])?)?$
^^^                                ^^^^^

https://regex101.com/r/xCxSn4/1

注意,如果要匹配纯空格,可以在模式中只使用纯空格,不需要字符集:例如([ ])等效于( )

使用pcre,另一种选择是使用条件来检查是否存在具有该形式的组 1。

(?(1)foo|bar)

对于示例数据,您可以将所有 3 个部分设置为可选,其中第一部分是捕获组。如果没有捕获组 1,则匹配最后一个部分。

^M[01](h*C[1-5])?(?:h*([A-Z]{3}))?(?(1)|(?:h*C[1-5])?)$

解释

  • ^字符串开头
  • M[01]匹配 M 和 0 或 1
  • (捕获组 1
    • h*C[1-5]匹配 0+ 水平空格字符和数字 1-5 的 C
  • )?关闭组 1 并将其设置为可选
  • (?:非捕获组
    • h*([A-Z]{3})匹配0+水平空格字符和A-Z之间的3次
  • )?关闭组并将其设置为可选
  • (?If 子句
    • (1)测试捕获组 1 是否存在。如果是这样,则什么都不做
    • |
    • (?:h*C[1-5])?可选匹配 0+ 水平空格字符和 C 与数字 1-5
  • )关闭如果子句
  • $字符串结尾

正则表达式演示

请注意,在您尝试的模式中,匹配左括号和右括号是可选的)?也可能匹配M1 (OSS)。不确定这是否是预期的匹配,但我省略了那部分。

最新更新