REGEX-需要在多个字符串后面进行否定查找的方法

时间:2018-08-22 11:30:52

标签: regex preg-match

我正在尝试匹配文件名中不需要的区域以删除文件。

如果REGEX找到一个“坏区域” (Brazil or Columbia),但我想得到任何匹配项,但是如果它们与同一括号(USA, UK, Europe, Australia)中的“好区域”混在一起,我想得到任何匹配项。

我有一个正则表达式

(?<![( ](USA)[,)])[( ](Brazil|Columbia)[,)](?![( ](USA|UK|Europe|Australia)[,)])

FIFA Soccer (USA, Brazil)      <<< DON't MATCH IF USA IS IN SAME BRACKET BEFORE
FIFA Soccer (Brazil, USA)      <<< DON't MATCH IF USA IS IN SAME BRACKET AFTER
FIFA Soccer (Brazil)           <<< MATCH
FIFA Soccer (Brazil, Ireland)  <<< MATCH
FIFA Soccer (Moon, Brazil)     <<< MATCH

到目前为止,正确的行是匹配的,但这是因为我有一个固定宽度的“ negative lookbehind”在寻找“ USA”……但是我也希望在我的国家中包含“ UK”,“ Europe”和“ Australia”负面的回望,我不能这样做,因为它们必须是“固定宽度” ...

FIFA Soccer (UK, Brazil) <<< ERROR - THIS ONE SHOULDN'T MATCH AND DOES
FIFA Soccer (Brazil, UK) <<< This one works (no match) because I have my lookahead set up

观看现场演示: Here

因此,有一种方法可以使REGEX开头的(?<![( ](USA|UK|Europe|Australia)[,)])之类的东西生效,以使UK, BrazilEurope, Brazil之类的东西不匹配。

3 个答案:

答案 0 :(得分:0)

您可以使用

\((?!(?:[^()]*,\s*)?(?:USA|UK|Europe|Australia)\s*[,)])[^()]*\)

请参见regex demo

详细信息

  • \(-一个(字符
  • (?!(?:[^()]*,\s*)?(?:USA|UK|Europe|Australia)\s*[,)])-负前瞻,如果在右侧立即有匹配项,则匹配失败
    • (?:[^()]*,\s*)?-的可选序列
      • [^()]*-除()以外的0多个字符
      • ,-逗号
      • \s*-超过0个空格
    • (?:USA|UK|Europe|Australia)-好的价值之一
    • \s*-超过0个空格
    • [,)]-一个,)
  • [^()]*-除()以外的0个或更多字符
  • \)-一个)字符。

答案 1 :(得分:0)

您可以交替使用PCRE动词(*SKIP)(*F)来匹配和拒绝匹配,而不是使用可变长度的负向查找:

(?:USA|UK|Europe|Australia),\h*(?:Brazil|Austria)[,)](*SKIP)(*F)|(?:Brazil|Austria)[,)](?!\h?(?:USA|UK|Europe|Australia)[,)])

Updated RegEx Demo

  • (*FAIL)的行为类似于失败的否定断言,并且是(?!)的同义词
  • (*SKIP)定义了一个点,当子模式稍后失败时,不允许正则表达式引擎回溯
  • (*SKIP)(*FAIL)一起提供了一个很好的限制选择,即您不能在上述正则表达式中留有可变长度。

您可以在PCRE中使用DEFINE动词来避免正则表达式中的重复,如下所示:

/
(?(DEFINE) # use define to avoid repetitions
  (?<ct>USA|UK|Europe|Australia) # disallow countries
  (?<mct>Brazil|Austria) # matching countries
)
# main regex starts here
(?&ct),\h*(?&mct)[,)](*SKIP)(*F)
|
(?&mct)[,)](?!\h?(?&ct)[,)])
/x

RegEx Demo 2

答案 2 :(得分:0)

使用模式提取国家/地区名称和array_filter

$filenames = ['FIFA Soccer (USA, Brazil)',
              'FIFA Soccer (Brazil, USA)',
              'FIFA Soccer (Brazil)',
              'FIFA Soccer (Brazil, Ireland)',
              'FIFA Soccer (Moon, Brazil)'];

$bad = ['Brazil', 'Columbia'];
$good = ['USA', 'UK', 'Europe', 'Australia'];

$todelete = array_filter($filenames, function ($i) use ($bad, $good) {
    $countries = preg_match_all('~(?:\G(?!\A), |\()\K\pL+~', $i, $m) ? $m[0] : [];
    return array_intersect($countries, $bad) && !array_intersect($countries, $good);
});

print_r($todelete);