要匹配的正则表达式模式,不包括...... /除外

Han*_*ler 104 regex

- 编辑 - 目前的答案有一些有用的想法,但我想要一些更完整的东西,我可以100%理解和重用; 这就是我设置赏金的原因.对于我来说,无处不在的想法比没有标准语法更好\K

这个问题是关于如何匹配模式除了某些情况s1 s2 s3.我举一个具体的例子来说明我的意思,但更喜欢我能100%理解的一般答案,所以我可以在其他情况下重复使用它.

我希望匹配五位数\b\d{5}\b但不能在三种情况下匹配s1 s2 s3:

s1:不在以句子这样的句号结尾的行上.

s2:在parens里面没有任何地方.

s3:不在以#开头if(和结尾的块内//endif

我知道如何使用前瞻和后视来解决s1 s2 s3中的任何一个,尤其是在C#lookbehind或\KPHP中.

例如

S1 (?m)(?!\d+.*?\.$)\d+

s3与C#lookbehind (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b

s3与PHP\K (?:(?:if\(.*?//endif)\D*)*\K\d+

但条件的混合使我的头脑爆炸.更糟糕的是,我可能需要在其他时间添加其他条件s4 s5.

好消息是,我不在乎是否使用PHP,C#,Python或邻居的洗衣机等大多数常用语言处理文件.:)我几乎是Python和Java的初学者,但有兴趣了解它是否有解决方案.

所以我来到这里看是否有人想到一个灵活的食谱.

提示没问题:你不需要给我完整的代码.:)

谢谢.

zx8*_*x81 201

汉斯,我会接受诱饵并充实我早先的答案.你说你想要"更完整的东西",所以我希望你不会介意那么长的答案 - 只是想取悦.让我们从一些背景开始.

首先,这是一个很好的问题.除了在某些上下文中(例如,在代码块内或括号内),通常存在关于匹配某些模式的问题.这些问题往往会产生相当尴尬的解决方案.所以关于多种情境的问题是一个特殊的挑战.

令人惊讶的是,至少有一种有效的解决方案是通用的,易于实施且易于维护.它适用于所有正则表达式,允许您检查代码中的捕获组.它碰巧回答了一些常见的问题,这些问题可能与你的问题有所不同:"除了甜甜圈之外的所有东西","除了...之外的全部替换","除了我母亲的黑名单上的所有单词之外","忽略"标签","匹配温度,除非斜体"......

遗憾的是,这项技术并不为人所知:我估计在20个可以使用它的SO问题中,只有一个有一个答案提到它 - 这意味着可能是五十或六十个答案中的一个.请参阅我在评论中与Kobi的交流.本文深入介绍了该技术,并将其称之为(乐观地)"有史以来最好的正则表达技巧".在没有详细介绍的情况下,我将尽力让您牢牢掌握该技术的工作原理.有关各种语言的更多详细信息和代码示例,我建议您查阅该资源.

一个更好的变化

使用Perl和PHP特有的语法可以实现相同的变体.你会在正则表达式等名家之手SO看到它CasimiretHippolyte哈姆扎.我将在下面详细介绍这一点,但我的重点是适用于所有正则表达式的一般解决方案(只要您可以检查代码中的捕获组).

感谢所有的背景,zx81 ......但是配方是什么?

关键事实

该方法返回组1捕获中的匹配.它根本不关心整体比赛.

实际上,诀窍是匹配我们不想要的各种上下文(使用|OR /交替链接这些上下文)以便"中和它们".匹配所有不需要的背景之后,交替的最后一部分相匹配,我们什么想要,它捕捉到组1.

一般食谱是

Not_this_context|Not_this_either|StayAway|(WhatYouWant)
Run Code Online (Sandbox Code Playgroud)

这将匹配Not_this_context,但在某种意义上,匹配进入垃圾箱,因为我们不会查看整体匹配:我们只查看第1组捕获.

在您的情况下,您的数字和三个上下文要忽略,我们可以这样做:

s1|s2|s3|(\b\d+\b)
Run Code Online (Sandbox Code Playgroud)

请注意,因为我们实际上匹配s1,s2和s3而不是试图通过外观来避免它们,所以s1,s2和s3的各个表达式可以保持清晰.(它们是a的每一边的子表达式|)

整个表达式可以这样写:

(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)
Run Code Online (Sandbox Code Playgroud)

请参阅此演示(但请关注右下方窗格中的捕获组.)

如果你在心理上试图在每个|分隔符处拆分这个正则表达式,它实际上只是一系列四个非常简单的表达式.

对于支持自由间距的口味,这读起来特别好.

(?mx)
      ### s1: Match line that ends with a period ###
^.*\.$  
|     ### OR s2: Match anything between parentheses ###
\([^\)]*\)  
|     ### OR s3: Match any if(...//endif block ###
if\(.*?//endif  
|     ### OR capture digits to Group 1 ###
(\b\d+\b)
Run Code Online (Sandbox Code Playgroud)

这非常容易阅读和维护.

扩展正则表达式

当你想忽略更多情况s4和s5时,你可以在左边的更多变化中添加它们:

s4|s5|s1|s2|s3|(\b\d+\b)
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?

您不想要的上下文被添加到左侧的替换列表中:它们将匹配,但是从不检查这些总体匹配,因此匹配它们是将它们放入"垃圾箱"的一种方式.

但是,您想要的内容将被捕获到组1.然后,您必须以编程方式检查组1是否已设置且不为空.这是一个简单的编程任务(我们稍后将讨论它是如何完成的),特别是考虑到它为您提供了一个简单的正则表达式,您可以一目了然地进行修改或根据需要进行修改或扩展.

我并不总是视觉化的粉丝,但是这个方法很好地展示了方法的简单性.每条"线"对应一个潜在的匹配,但只有底线被捕获到组1中.

正则表达式可视化

Debuggex演示

Perl/PCRE变异

与上面的一般解决方案相反,Perl和PCRE存在一种变体,通常可以在SO上看到,至少在正则表达神的手中如@CasimiretHippolyte和@HamZa.它是:

(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant
Run Code Online (Sandbox Code Playgroud)

在你的情况下:

(?m)(?:^.*\.$|\([^()]*\)|if\(.*?//endif)(*SKIP)(*F)|\b\d+\b
Run Code Online (Sandbox Code Playgroud)

这种变化更容易使用,因为在上下文s1,s2和s3中匹配的内容被简单地跳过,因此您不需要检查第1组捕获(注意括号已经消失).匹配仅包含whatYouWant

请注意(*F),(*FAIL)并且(?!)都是一样的.如果你想更加模糊,你可以使用(*SKIP)(?!)

这个版本的演示

应用

以下是该技术通常可以轻松解决的一些常见问题.你会发现选择这个词可以使这些问题听起来有些不同,而事实上它们几乎完全相同.

  1. 除了像标签中的任何地方,我如何匹配foo <a stuff...>...</a>
  2. 我如何匹配foo,除了<i>标签或javascript片段(更多条件)?
  3. 如何匹配此黑名单上没有的所有单词?
  4. 如何忽略SUB ... END SUB块内的任何内容?
  5. 除了... s1 s2 s3,我怎么能匹配所有东西?

如何编程第1组捕获

您没有代码,但是,为了完成...检查组1的代码显然取决于您选择的语言.无论如何,它不应该为用于检查匹配的代码添加多行.

如果有疑问,我建议您查看前面提到的文章的代码示例部分,它提供了很多语言的代码.

备择方案

根据问题的复杂程度以及所使用的正则表达式引擎,有几种选择.以下是适用于大多数情况的两种情况,包括多种情况.在我看来,s1|s2|s3|(whatYouWant)如果只是因为清晰度总是胜出,那么它们几乎都不如配方那么有吸引力.

1.替换然后匹配.

一个听起来很糟糕但在许多环境中运行良好的好解决方案是分两步完成.第一个正则表达式通过替换可能存在冲突的字符串来中和您要忽略的上下文.如果您只想匹配,则可以使用空字符串替换,然后在第二步中运行匹配.如果你想要替换,你可以先用一些独特的东西替换要忽略的字符串,例如用固定宽度的链条围绕你的数字@@@.在更换之后,您可以自由地替换您真正想要的东西,然后您将必须恢复您的独特@@@字符串.

2.外观.

您的原始帖子显示您了解如何使用外观排除单个条件.你说C#很适合这个,而你是对的,但它不是唯一的选择.例如,在C#,VB.NET和Visual C++中发现的.NET正则表达式风格,以及在Python中regex替换的仍在实验的模块re,是我所知道的仅支持无限宽度外观的两个引擎.使用这些工具,一个后视镜中的一个条件可以不仅在后面而且在比赛中以及在比赛之后进行观察,从而避免需要与前瞻相协调.更多条件?更多的外观.

在C#中回收你对s3的正则表达式,整个模式看起来像这样.

(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
Run Code Online (Sandbox Code Playgroud)

但到现在你知道我不推荐这个,对吧?

缺失

@HamZa和@Jerry建议我在你试图删除时提到一个额外的技巧WhatYouWant.你记得匹配的配方WhatYouWant(将其捕获到第1组)是s1|s2|s3|(WhatYouWant)吧?要删除所有实例WhatYouWant,请将正则表达式更改为

(s1|s2|s3)|WhatYouWant
Run Code Online (Sandbox Code Playgroud)

对于替换字符串,请使用$1.这里发生的是,对于s1|s2|s3匹配的每个实例,替换$1将替换该实例与其自身(引用$1).另一方面,当WhatYouWant匹配时,它被一个空组替换而不是其他 - 因此被删除.看到这个演示,感谢@HamZa和@Jerry建议这个精彩的补充.

更换

这给我们带来了替代,我将简要介绍一下.

  1. 如果没有替换,请参阅上面的"删除"技巧.
  2. 更换时,如果使用Perl或PCRE,请使用(*SKIP)(*F)上面提到的变体来准确匹配您想要的变体,并进行直接替换.
  3. 在其他类型中,在替换函数调用中,使用回调或lambda检查匹配,如果设置了组1则替换.如果您需要帮助,已经引用的文章将为您提供各种语言的代码.

玩得开心!

不,等等,还有更多!

啊,不,我会在二十卷中为我的回忆录保存,将于明年春天发布.

  • @Kobi两部分回复.是的,昨晚写完了,在底部写道,我会睡觉,然后整理一下.:)是的诀窍很简单,但我并不认为它是"基本的",因为它似乎不是人们用来解决排除问题的常用工具的一部分.当我搜索SO上的"除外"或"除非"或"不在内部"问题时,只有一个答案(没有投票)表明它,其他人都没有.顺便说一句,我没有看到你的答案,这些答案非常棒.:) (2认同)
  • 抱歉,但 Rex 的“最佳技巧”根本不起作用(_可靠地_)。假设你想匹配`Tarzan`,但不是双引号内的任何地方。:`/no|no|(yes)/` 技巧正则表达式将类似于:`/"[^"]*"|Tarzan/`(忽略转义字符)。这在许多情况下都有效,但在以下情况下完全失败应用于以下有效的 JavaScript 文本:`var bug1 = 'One " 此处引用。应该匹配这个泰山'; var bug2 = "不应该匹配这个泰山";`. Rex 的技巧仅在所有可能的结构都匹配时才有效 - 换句话说 - 您需要完全解析文本以保证 100% 的准确性。 (2认同)

Yaw*_*war 11

进行三种不同的匹配,并使用程序中条件逻辑处理三种情况的组合.你不需要处理一个巨大的正则表达式中的所有东西.

编辑:让我扩大一点,因为问题变得更有趣:-)

您在此尝试捕获的一般想法是匹配某个正则表达式模式,但不是在测试字符串中存在某些其他(可能是任何数字)模式时.幸运的是,您可以利用您的编程语言:保持正则表达式简单,只需使用复合条件.最好的做法是在可重用的组件中捕获这个想法,所以让我们创建一个实现它的类和方法:

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class MatcherWithExceptions {
  private string m_searchStr;
  private Regex m_searchRegex;
  private IEnumerable<Regex> m_exceptionRegexes;

  public string SearchString {
    get { return m_searchStr; }
    set {
      m_searchStr = value;
      m_searchRegex = new Regex(value);
    }
  }

  public string[] ExceptionStrings {
    set { m_exceptionRegexes = from es in value select new Regex(es); }
  }

  public bool IsMatch(string testStr) {
    return (
      m_searchRegex.IsMatch(testStr)
      && !m_exceptionRegexes.Any(er => er.IsMatch(testStr))
    );
  }
}

public class App {
  public static void Main() {
    var mwe = new MatcherWithExceptions();

    // Set up the matcher object.
    mwe.SearchString = @"\b\d{5}\b";
    mwe.ExceptionStrings = new string[] {
      @"\.$"
    , @"\(.*" + mwe.SearchString + @".*\)"
    , @"if\(.*" + mwe.SearchString + @".*//endif"
    };

    var testStrs = new string[] {
      "1." // False
    , "11111." // False
    , "(11111)" // False
    , "if(11111//endif" // False
    , "if(11111" // True
    , "11111" // True
    };

    // Perform the tests.
    foreach (var ts in testStrs) {
      System.Console.WriteLine(mwe.IsMatch(ts));
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

所以上面,我们设置了搜索字符串(五位数),多个异常字符串(你的s1,s2s3),然后尝试匹配几个测试字符串.打印结果应如每个测试字符串旁边的注释中所示.

  • 你的意思是可能连续匹配三个正则表达式?正则表达式1消除情况1(可能只是删除坏数字),r2删除s2,r3删除s3并匹配数字左?这是有趣的想法. (2认同)

归档时间:

查看次数:

21562 次

最近记录:

6 年,3 月 前