【问题标题】:REGEX IF THEN ELSE StatementREGEX IF THEN ELSE 语句
【发布时间】:2017-09-26 17:49:42
【问题描述】:

我需要编写一个让我摸不着头脑的正则表达式。本质上,我有一列数据,其中包含以下值:

ACME Corp 123
Corp 742 ACME
Random Text
Broadway 1785 FB

我想做的是寻找术语ACMEBROADWAY。如果其中任何一个存在,请仅保留那个。如果两者都不存在,则保留整个字符串。所以上面的那一列会变成:

ACME
ACME
Random Text
Broadway

这有意义吗?

【问题讨论】:

  • 你还在为使用正则表达式而苦恼吗?任何其他解析替代方案可以像 Python 一样让生活更轻松?
  • 无论您在做什么,使用 if elifelse 语句都比使用正则表达式要容易得多。您需要使用纯正则表达式有什么特别的原因吗?你的代码是什么语言的?
  • 如果您有要提取的实体列表(在您的情况下是公司,看起来像),为什么不检查它们是否存在于字符串中?喜欢if 'ACME' in 'ACME Corp 123'...
  • 我可以很容易地在 SQL 中将其作为 CASE STATEMENT 执行,并且将在 5 秒内完成。但是,我使用的这个特定工具集将我限制为 REGEX。
  • 纳什维尔的家伙,嗯?

标签: regex


【解决方案1】:

简介

这个让我有点摸不着头脑。我确信单独的正则表达式不是解决这个问题的最佳方法,但是,这是你的解决方案。


代码

See this code in use here

正则表达式

^.*?((?(?=.*?(\b(?:broadway|acme)\b).*?)\2|.*)).*?$

替换

第 1 组如下。您可以改为从匹配数组中收集第 1 组变量,但如果要替换,可以使用以下

$1

结果

注意:我添加了另一个字符串作为测试,以确保如果任何一个单词放在一行的中间,它仍然会捕获它。

输入

ACME Corp 123
Corp 742 ACME
Some ACME some
Random Text
Broadway 1785 FB

输出

ACME
ACME
ACME
Random Text
Broadway

说明

使用不区分大小写的i 和多行m 标志:

  • ^在行首断言位置
  • .*? 匹配任意字符任意次数,但越少越好
  • ((?(?=.*?(\b(?:broadway|acme)\b).*?)\2|.*))碎成碎片
    • ()以下截图
      • (?(?=...))if/else 语句
      • (?=.*?(\b(?:broadway|acme)\b).*?) 正向前瞻匹配以下
        • .*? 任意字符任意次数,但越少越好
        • (...) 将以下内容捕获到捕获组 2 中
        • \b(?:broadway|acme)\b 字边界,后跟broadwayacme,后跟字边界
        • .*? 任意字符任意次数,但越少越好
      • \2 如果 if/else 语句为 true(它与上述匹配),则捕获组(如上所述) - 即简单的 broadwayacme
      • .* 如果 if/else 语句为 false,则匹配任意字符任意次数
  • .*? 匹配任意字符任意次数,但越少越好
  • $在行尾断言位置

——

更新

由于我的回答引起了相当大的关注,我想我应该修改它。不确定是否关注正则表达式中的 if/else,或者它是否与样本输入中 OP 的预期结果更相关。

如果/否则

我应该提一下,正则表达式 if/else 的一般格式如下(并且只有某些正则表达式引擎支持此标签):

(?(?=condition)x|y)

在上面的正则表达式中 (?=condition) 几乎可以是你想要的任何东西(你也可以使用负前瞻或后瞻,甚至是它们的组合。

替代品

好像不是所有语言都支持正则表达式中的 if/else,您可以使用一种解决方法:

# optional group, fallback to match all (x?y)
^(?:.*?\b(broadway|acme)\b)?.*

# alternation (x||y)
^(?:.*?\b(broadway|acme)\b|.*)

# tempered greedy token alternation
^(?:(?!\b(?:broadway|acme)\b).|(broadway|acme))+

# same as above reusing capture group 1’s definition 
^(?:(?!\b(broadway|acme)\b).|((?1)))+

【讨论】:

  • 漂亮的方法。很高兴知道周围有人可以教我们一些东西。 :)
【解决方案2】:

足以解决此问题的正则表达式是:

 ^(?(?=(acme|broadway))\1|[\w\s])+?$

为什么这就足够了?如果 acmebroadway 在您的输入字符串中,则第 1 组将捕获该值。如果第 1 组为空,则完整匹配是您的结果。

细分:

 ^(?                          # start conditional
    (?=                       # lookahead for position before
      (                       # group 1 start
        acme|broadway         # either "acme" or "broadway"
      )                       # group 1 end
    )
    \1                        # if found, then match group 1
    |                         # else
    [\w\s]                    # read a word char or space
  )+?$                        # do this over and over again, non-greedy 

看看example 1

【讨论】:

  • 没有。细看。或者看看这个:regex101.com/r/6KZ81f/3
  • 您的正则表达式似乎无效。
  • 请多一点上下文。如果您说正则表达式无效,我建议您检查链接。如果您的陈述是“这不能回答 OP 的问题”,请解释您为什么这么认为。
【解决方案3】:

这是另一个尝试:

(?:^.*)(ACME)(?:.*$)?|(?:^.*)(Broadway)(?:.*$)|^.*$

还有正则表达式code in use

它与 Marc Lambrichs 解决方案很接近,但使用了两个捕获组(可以说更糟 - 但这取决于您的需求)。如果两个组($1 或 $2)都没有匹配项,您将在完整匹配项中找到随机文本。

如果你不喜欢第二个捕获组,你可以试试这个:

(?:^.*?)(ACME|Broadway)(?:.*$)?|^.*?$

或者,如果您想像 ctwheels 解决方案一样将所有内容都放在 $1 中:

(?(?=(?:^.*?)?(ACME|Broadway)(?:.*$)?)\1|(^.*?$))

正如 Marc 所指出的,我的方法的一个优点是它不需要所有正则表达式引擎中不可用的高级功能。
但是,第三个正则表达式中使用的conditional Regex 并非在任何地方都可用。

【讨论】:

  • 是的,情况更糟。因为现在您必须检查第 1 组和第 2 组才能知道您是否没有匹配项。在所有情况下。
  • 正如我所说的。我本可以调整它,但由于我们没有得到 OP 的任何回应,所以我觉得这毫无意义。
【解决方案4】:

另一个解决方案没有使用前瞻断言

^.*(ACME|Broadway).*$

解释:

^                     # beginning of the string
 .*                   # match any character any number of times
   (                  # start of capture group
    ACME|Broadway     # if the input string has ACME or Broadway capture in the memory($1)
   )                  # end of the capture group
 .*                   # match any character any number of times
$                     # end of the string

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

此外,您可以在 Javascript 中使用相同的正则表达式,如下所示

'ACME Corp 123'.replace(/^.*(ACME|Broadway).*$/, '$1');    // ACME
'Corp 742 ACME'.replace(/^.*(ACME|Broadway).*$/, '$1');    // ACME
'Random Text'.replace(/^.*(ACME|Broadway).*$/, '$1');      // Random Text
'Broadway 1785 FB'.replace(/^.*(ACME|Broadway).*$/, '$1'); // Broadway 

【讨论】: