我需要编写一个正则表达式,这让我很困惑。基本上,我有一列包含如下数值的数据:
ACME Corp 123
Corp 742 ACME
Random Text
Broadway 1785 FB
我想要做的是查找术语ACME
和BROADWAY
。如果两者都存在,则只保留它们。如果两者都不存在,则保留整个字符串。因此,该列将变为:
ACME
ACME
Random Text
Broadway
那样说你明白了吗?
我需要编写一个正则表达式,这让我很困惑。基本上,我有一列包含如下数值的数据:
ACME Corp 123
Corp 742 ACME
Random Text
Broadway 1785 FB
我想要做的是查找术语ACME
和BROADWAY
。如果两者都存在,则只保留它们。如果两者都不存在,则保留整个字符串。因此,该列将变为:
ACME
ACME
Random Text
Broadway
那样说你明白了吗?
这个问题曾经让我感到困惑。我相信仅使用正则表达式并不是解决这个问题的最佳方法,但以下是解决方案。
正则表达式
^.*?((?(?=.*?(\b(?:broadway|acme)\b).*?)\2|.*)).*?$
替换
下面是第一个分组。您可以从匹配数组中收集第一个分组变量,但如果想要替换,可以使用以下方法。
$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
单词边界,后跟broadway或acme,再后跟单词边界.*?
任意数量的任意字符,但尽可能少地匹配\2
如果if / else语句为true(它匹配上述内容),则捕获该组(如上所述)-即只有broadway
或acme
.*
如果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)))+
^(?(?=(acme|broadway))\1|[\w\s])+?$
为什么这样就足够了呢?如果你的输入字符串中包含acme
或者broadway
,那么第一组将会捕获这个值。如果第一组为空,则完整匹配就是你的结果。
分解:
^(? # 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
不使用前瞻断言的另一种解决方案
^.*(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
(?:^.*)(ACME)(?:.*$)?|(?:^.*)(Broadway)(?:.*$)|^.*$
并且正则表达式正在使用的代码。
这个解决方案与Marc Lambrichs的解决方案相似,但使用了两个捕获组(这可能更糟糕——但这取决于您的需求)。如果两个组中没有一个匹配,您将在完整匹配中找到随机文本。
如果您不喜欢第二个捕获组,可以尝试这个:
(?:^.*?)(ACME|Broadway)(?:.*$)?|^.*?$
或者,如果您想像ctwheels的解决方案一样将所有内容放在$1中:
(?(?=(?:^.*?)?(ACME|Broadway)(?:.*$)?)\1|(^.*?$))
if
、elif
和else
语句,而不是正则表达式,那么这将会更容易。你需要使用纯正则表达式的特定原因是什么?你的代码是用哪种语言编写的? - Davy Mif 'ACME' in 'ACME Corp 123'
... - blacksite