要匹配的正则表达式模式,不包括……/


108

-编辑 -当前的答案有一些有用的想法,但我希望我能100%理解和重用一些更完整的内容;这就是我设置赏金的原因。同样,无处不在的想法对我来说比不像标准语法更好\K

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

我想使用\b\d{5}\bs1 s2 s3但在三种情况下不匹配五个数字:

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

s2:不在parens内的任何地方。

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

我知道如何用超前和后向方式解决s1 s2 s3中的任何一个,尤其是在C#后向或\KPHP中。

例如

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

带有C#后视的s3 (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b

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

但是,各种条件的综合使我的脑袋爆炸了。更糟糕的是,我可能需要在其他时间添加其他条件s4 s5。

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

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

提示还可以:您不需要给我完整的代码。:)

谢谢。


1
\K没有特殊的php语法。请详细说明您要说的话。如果您想告诉我们不需要“复杂”的解决方案,则必须说出什么对您来说很复杂以及为什么。
hakre 2014年

@hakre您的意思是因为ruby现在正在使用它,并且它始于perl?
汉斯·辛德勒

1
不,因为不是PHP(也不是Ruby)的PCRE。Perl不同,但是PCRE的目标是与Perl Regex兼容。
hakre 2014年

您的s2和s3要求似乎是矛盾的。s2意味着括号总是匹配的并且可以嵌套,但是s3要求"if("关闭:open括号,而不是使用a ")",而是使用:"//endif"?如果对于s3,您实际上是说if子句应使用:结束"//endif)",则s3要求是s2的子集。
ridgerunner 2014年

@hakre是的,我知道PCRE,但是要解释一下,问题是关于编程语言的……它说的especially in C# lookbehind or \K in PHP……但是C#不仅是C#,而且是.NET,所以您也可以抱怨我说C#而不是.NET :) Ruby不是Onigurama也很糟糕...是否有另一种使用PCRE的语言?不谈论Notepad ++或服务器工具,这是有关使用语言功能的问题,我希望解释并抱歉,如果它看起来不正确
Hans Schindler 2014年

Answers:


205

汉斯,我将诱饵充实我先前的回答。您说过您想要“更完整的东西”,所以我希望您不会介意冗长的答案,只是想取悦他人。让我们从一些背景开始。

首先,这是一个很好的问题。除某些上下文外(例如,在代码块内或括号内),经常会遇到有关匹配某些模式的问题。这些问题常常引起相当尴尬的解决方案。因此,您关于多个上下文的问题是一个特殊的挑战。

惊喜

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

可悲的是,该技术并不为人所知:我估计在二十个可以使用它的SO问题中,只有一个回答提到了它-这意味着可能有五十或六十个回答。在评论中查看我与Kobi的交流。该技术是在一些深入的描述本文这就要求它(乐观)“最好的正则表达式招过”。在不赘述的情况下,我将尝试为您牢牢掌握该技术的工作原理。有关更多详细信息和各种语言的代码示例,建议您查阅该资源。

更好的变化

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

感谢所有背景,zx81 ...但是有什么秘诀?

关键事实

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

实际上,诀窍是匹配我们不需要的各种上下文(使用|OR /交替链接这些上下文)以“中和它们”。匹配所有不需要的背景之后,交替的最后部分匹配我们希望它捕获到组1。

一般配方是

Not_this_context|Not_this_either|StayAway|(WhatYouWant)

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

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

s1|s2|s3|(\b\d+\b)

请注意,因为我们实际上是将s1,s2和s3匹配,而不是尝试通过环视来避免它们,所以s1,s2和s3的各个表达式可以整天保持清晰。(它们是的每一边的子表达式|

整个表达式可以这样写:

(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)

请参阅此演示(但请注意右下窗格中的捕获组。)

如果您在思维上尝试在每个|定界符处拆分此正则表达式,则实际上它只是一系列四个非常简单的表达式。

对于支持自由调配的口味,这尤其好。

(?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)

这非常容易阅读和维护。

扩展正则表达式

当您想忽略s4和s5的更多情况时,可以在左侧的更多交替中添加它们:

s4|s5|s1|s2|s3|(\b\d+\b)

这是如何运作的?

不需要的上下文会添加到左侧的替代列表中:它们会匹配,但是不会检查这些总体匹配,因此匹配它们是一种将它们放入“垃圾箱”的方法。

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

我并不总是喜欢可视化,但是这很好地展示了该方法的简单性。每条“线”对应于一个潜在的匹配,但只有底线被捕获到组1中。

正则表达式可视化

Debuggex演示

Perl / PCRE变化

与上面的一般解决方案相反,Perl和PCRE存在一个变体,至少在正则表达式神之手(如@CasimiretHippolyte和@HamZa)上经常出现在SO上。它是:

(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant

在您的情况下:

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

使用此变体要容易一些,因为只跳过了上下文s1,s2和s3中匹配的内容,因此您无需检查第1组捕获的内容(注意括号已消失)。比赛只包含whatYouWant

请注意(*F)(*FAIL)(?!)都是一样的东西。如果您想变得更晦涩,可以使用(*SKIP)(?!)

此版本的演示

应用领域

这是该技术通常可以轻松解决的一些常见问题。您会注意到,选择这个词可能会使其中一些问题听起来有所不同,而实际上它们实际上是相同的。

  1. 如何匹配foo之类的标记之外的foo <a stuff...>...</a>
  2. 除了<i>标签或JavaScript代码段(更多条件)外,如何匹配foo ?
  3. 如何匹配不在此黑名单上的所有单词?
  4. 如何忽略SUB ... END SUB块中的任何内容?
  5. 除了s1 s2 s3之外,我该如何匹配所有内容?

如何对第1组捕获进行编程

您不是要编写代码,而是要完成代码……检查第1组的代码显然取决于您选择的语言。无论如何,它不应在用于检查匹配项的代码中添加多行。

如有疑问,建议您查看代码示例部分前面提到的文章,其中提供了多种语言的代码。

备择方案

根据问题的复杂性以及所使用的正则表达式引擎,有几种选择。这是可以适用于大多数情况(包括多种情况)的两种情况。我认为,这两种方法都没有像s1|s2|s3|(whatYouWant)配方,仅仅是因为清晰度总是会胜出。

1.替换然后匹配。

一个听起来不错但在许多环境中都能正常工作的好的解决方案是分两个步骤工作。第一个正则表达式通过替换潜在冲突的字符串来中和您要忽略的上下文。如果只想匹配,则可以用空字符串替换,然后在第二步中运行匹配。如果要替换,您可以先用与众不同的东西替换要忽略的字符串,例如用固定宽度的包围数字@@@。替换之后,您可以随意替换您真正想要的内容,然后必须还原您独特的@@@字符串。

2.环顾四周。

您的原始帖子显示,您了解如何使用环顾四周排除单个条件。您说过C#对此非常有用,您说的没错,但这不是唯一的选择。例如,在C#,VB.NET和Visual C ++中找到的.NET regex风格,以及在Python中仍要regex替换的仍在实验中的模块re,是我所知道的仅有的两个支持无限宽后向搜索的引擎。借助这些工具,在后方看一眼的情况不仅可以照顾比赛的后面,而且可以看比赛以及超越比赛,而无需协调前瞻。还有更多条件吗?更多环顾四周。

回收C#中用于s3的正则表达式,整个模式如下所示。

(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b

但是现在您知道我不推荐这样做了,对吧?

删除项

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

(s1|s2|s3)|WhatYouWant

对于替换字符串,请使用$1。在这里发生的是,对于s1|s2|s3匹配的每个实例,替换$1将用自身替换该实例(由引用$1)。另一方面,当WhatYouWant被匹配时,它被一个空组代替,别无其他,因此被删除。观看此演示,谢谢@HamZa和@Jerry提出了这一出色的建议。

替代品

这将带给我们替代品,我将在此简单介绍。

  1. 如果不进行任何替换,请参见上面的“删除”技巧。
  2. 替换时,如果使用Perl或PCRE,请使用(*SKIP)(*F)上面提到的变体完全匹配您想要的内容,然后进行直接替换。
  3. 在其他情况下,在替换函数调用中,使用回调或lambda检查匹配项,并替换是否设置了组1。如果您需要帮助,已经参考的文章将为您提供各种语言的代码。

玩得开心!

不,等等,还有更多!

啊,不,我将其保存在二十卷的回忆录中,这些回忆录将于明年春季发行。


2
@Kobi两部分答复。是的,昨晚被带走了写作,并在底部写下我要睡在上面,然后整理一下。:)是的,技巧很简单,但我不同意您的看法,它是“基本的”,因为它似乎并不是人们用来解决排斥问题的常用工具的一部分。当我在SO上搜索“除”,“除非”或“不在内部”的问题时,只有一个答案(没有投票)建议这样做,其他答案都没有。顺便说一下,我还没有看到你的答案,这太好了。:)
zx81 2014年

2
抱歉,但是Rex的“最佳技巧”根本行不通(可靠)。说您要匹配Tarzan,但不要在双引号内的任何地方匹配。The:/no|no|(yes)/技巧正则表达式将类似于:(/"[^"]*"|Tarzan/忽略转义的字符)。这在很多情况下都可以使用,但是在应用于以下有效的JavaScript文本时会完全失败:var bug1 = 'One " quote here. Should match this Tarzan'; var bug2 = "Should not match this Tarzan";。Rex的技巧仅在所有可能的结构都匹配时才起作用-换句话说,您需要完全解析文本以确保100%的准确性。
ridgerunner 2014年

1
对不起,如果我听起来很刺耳-那当然不是我的意图。我的观点(就像我对上面原始问题的第二条评论一样)是,正确的解决方案高度依赖于要搜索的目标文本。我的示例将JavaScript源代码作为目标文本,其中在单引号字符串内包含一个双引号。它可能像:这样的文字RegExp一样容易,var bug1 = /"[^"]*"|(Tarzan)/gi;并且具有相同的效果(并且第二个示例当然不是边缘情况)。我可以举很多例子说明这种技术无法可靠工作的地方。
ridgerunner 2014年

1
@ridgerunner我总是很高兴收到您的来信,这听起来对我来说毫无道理。当我们知道我们的字符串可能包含“错误警报”时,我们都会调整模式。例如,要匹配可能包含转义引号的字符串,而该引号可能会使字符串匹配器(?<!\\)"(?:\\"|[^"\r\n])*+" 无法正常运行,则可以使用“ 除非您有理由,否则请不要用力”。解决方案的原理仍然有效。如果我们无法表达要放在左侧的模式,那就是另一回事了,我们需要一个不同的解决方案。但是该解决方案可以实现其广告宣传。
2014年

1
@funkwurm用户已将此答案添加到堆栈溢出正则表达式常见问题解答中
2014年

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));
    }
  }
}

因此,在上面,我们设置了搜索字符串(五个数字),多个异常字符串(您的s1s2s3),然后尝试与多个测试字符串进行匹配。打印结果应如每个测试字符串旁边的注释所示。


2
您是说像连续匹配三个正则表达式?正则表达式1消除情况1(也许只是删除错误的数字),r2删除s2,r3删除s3并匹配剩余的数字?那是个有趣的主意。
汉斯·辛德勒

哈,当然,这就是为什么我投票赞成你。:)不要误会我的意思,我仍然认为在这种情况下,我的回答更加有效和可维护。您看到我昨天添加的免费版本吗?这是一次通过,而且非常易于阅读和维护。但我确实喜欢您的工作和扩展的答案。抱歉,我无法再次投票,否则我会。:)
zx81

2

您对它不在内部的要求不可能满足所有情况。也就是说,如果您能以某种方式(在左侧和)右侧找到a ,并不总是意味着您位于parens内。例如。

(....) + 55555 + (.....)-不在括号内,(并且)在左右

现在,您可能会认为自己很聪明,(只有在)之前从未遇到过时才向左寻找,反之亦然。这不适用于这种情况:

((.....) + 55555 + (.....))-即使在左右两边都在关闭),在内部也是如此(

使用正则表达式无法确定您是否位于parens内部,因为regex无法计算已打开多少parens和关闭了多少parens。

考虑一下这个更简单的任务:使用正则表达式,找出字符串中所有(可能嵌套的)paren是否都关闭了,即针对(需要查找的每个对象)。您会发现这是不可能解决的,如果您不能使用正则表达式解决该问题,那么您将无法确定所有情况下单词是否都在括号内,因为如果出现这种情况,您将无法确定字符串中的某个位置所有的前面(都有一个对应的)


2
没有人说过有关嵌套括号的任何信息,而zx81的回答可以很好地处理您的情况#1。
丹·贝查德

谢谢您的好主意:)但嵌套括号并不使我担心这个问题,它更多地是关于糟糕的情况s1 s2 s3
Hans Schindler 2014年

当然不是不可能!这就是为什么您需要跟踪当前正在解析的paren级别的原因。
2014年

好吧,如果您正在像OP那样解析某种CFG,则最好通过生成没有问题的LALR或类似解析器来获得更好的服务。
RokL

2

汉斯,如果您不介意,我用了邻居的洗衣机perl :)

编辑: 下面是一个伪代码:

  loop through input
  if line contains 'if(' set skip=true
        if skip= true do nothing
        else
           if line match '\b\d{5}\b' set s0=true
           if line does not match s1 condition  set s1=true
           if line does not match s2 condition  set s2=true
           if s0,s1,s2 are true print line 
  if line contains '//endif' set skip=false

给定文件input.txt:

tiago@dell:~$ cat input.txt 
this is a text
it should match 12345
if(
it should not match 12345
//endif 
it should match 12345
it should not match 12345.
it should not match ( blabla 12345  blablabla )
it should not match ( 12345 )
it should match 12345

和脚本validator.pl:

tiago@dell:~$ cat validator.pl 
#! /usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

sub validate_s0 {
    my $line = $_[0];
    if ( $line =~ \d{5/ ){
        return "true";
    }
    return "false";
}

sub validate_s1 {
    my $line = $_[0];
    if ( $line =~ /\.$/ ){
        return "false";
    }
    return "true";
}

sub validate_s2 {
    my $line = $_[0];
    if ( $line =~ /.*?\(.*\d{5.*?\).*/ ){
        return "false";
    }
    return "true";
}

my $skip = "false";
while (<>){
    my $line = $_; 

    if( $line =~ /if\(/ ){
       $skip = "true";  
    }

    if ( $skip eq "false" ) {
        my $s0_status = validate_s0 "$line"; 
        my $s1_status = validate_s1 "$line";
        my $s2_status = validate_s2 "$line";

        if ( $s0_status eq "true"){
            if ( $s1_status eq "true"){
                if ( $s2_status eq "true"){
                    print "$line";
                }
            }
        }
    } 

    if ( $line =~ /\/\/endif/) {
        $skip="false";
    }
}

执行:

tiago @ dell:〜$ cat input.txt | Perl验证程序
它应该匹配12345
它应该匹配12345
它应该匹配12345

2

不知道这是否对您有帮助,但是考虑到以下假设,我正在提供一种解决方案-

  1. 您需要一个优雅的解决方案来检查所有条件
  2. 条件可能在将来和任何时间发生变化。
  3. 一种情况不应依赖其他条件。

但是我还考虑了以下内容-

  1. 给定的文件中的错误最少。如果确实如此,那么我的代码可能需要进行一些修改才能解决。
  2. 我使用Stack来跟踪if(块。

好的,这是解决方案-

我使用C#及其MEF(Microsoft可扩展性框架)来实现可配置的解析器。想法是,使用单个解析器进行解析,并使用一系列可配置的验证器类来验证行并基于验证返回true或false。然后,您可以随时添加或删除任何验证器,或者根据需要添加新的验证器。到目前为止,我已经为您提到的S1,S2和S3实现了,请检查第3点的类。如果将来需要,您必须为s4,s5添加类。

  1. 首先,创建接口-

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FileParserDemo.Contracts
    {
        public interface IParser
        {
            String[] GetMatchedLines(String filename);
        }
    
        public interface IPatternMatcher
        {
            Boolean IsMatched(String line, Stack<string> stack);
        }
    }
  2. 然后是文件读取器和检查器-

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using FileParserDemo.Contracts;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Collections;
    
    namespace FileParserDemo.Parsers
    {
        public class Parser : IParser
        {
            [ImportMany]
            IEnumerable<Lazy<IPatternMatcher>> parsers;
            private CompositionContainer _container;
    
            public void ComposeParts()
            {
                var catalog = new AggregateCatalog();
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(IParser).Assembly));
                _container = new CompositionContainer(catalog);
                try
                {
                    this._container.ComposeParts(this);
                }
                catch
                {
    
                }
            }
    
            public String[] GetMatchedLines(String filename)
            {
                var matched = new List<String>();
                var stack = new Stack<string>();
                using (StreamReader sr = File.OpenText(filename))
                {
                    String line = "";
                    while (!sr.EndOfStream)
                    {
                        line = sr.ReadLine();
                        var m = true;
                        foreach(var matcher in this.parsers){
                            m = m && matcher.Value.IsMatched(line, stack);
                        }
                        if (m)
                        {
                            matched.Add(line);
                        }
                     }
                }
                return matched.ToArray();
            }
        }
    }
  3. 然后是各个检查器的实现,类名是不言自明的,因此我认为它们不需要更多的描述。

    using FileParserDemo.Contracts;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;
    
    namespace FileParserDemo.PatternMatchers
    {
        [Export(typeof(IPatternMatcher))]
        public class MatchAllNumbers : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("\\d+");
                return regex.IsMatch(line);
            }
        }
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveIfBlock : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("if\\(");
                if (regex.IsMatch(line))
                {
                    foreach (var m in regex.Matches(line))
                    {
                        //push the if
                        stack.Push(m.ToString());
                    }
                    //ignore current line, and will validate on next line with stack
                    return true;
                }
                regex = new Regex("//endif");
                if (regex.IsMatch(line))
                {
                    foreach (var m in regex.Matches(line))
                    {
                        stack.Pop();
                    }
                }
                return stack.Count == 0; //if stack has an item then ignoring this block
            }
        }
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveWithEndPeriod : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("(?m)(?!\\d+.*?\\.$)\\d+");
                return regex.IsMatch(line);
            }
        }
    
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveWithInParenthesis : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("\\(.*\\d+.*\\)");
                return !regex.IsMatch(line);
            }
        }
    }
  4. 该程序 -

    using FileParserDemo.Contracts;
    using FileParserDemo.Parsers;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FileParserDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                var parser = new Parser();
                parser.ComposeParts();
                var matches = parser.GetMatchedLines(Path.GetFullPath("test.txt"));
                foreach (var s in matches)
                {
                    Console.WriteLine(s);
                }
                Console.ReadLine();
            }
        }
    }

为了进行测试,我使用了@Tiago的示例文件,Test.txt其中包含以下几行-

this is a text
it should match 12345
if(
it should not match 12345
//endif 
it should match 12345
it should not match 12345.
it should not match ( blabla 12345  blablabla )
it should not match ( 12345 )
it should match 12345

给出输出-

it should match 12345
it should match 12345
it should match 12345

不知道这是否对您有帮助,我确实玩得很开心.... :)

最好的部分是,要添加新条件,您所要做的就是提供的实现IPatternMatcher,它将自动被调用并因此进行验证。


2

与@ zx81相同,(*SKIP)(*F)但使用否定的超前断言。

(?m)(?:if\(.*?\/\/endif|\([^()]*\))(*SKIP)(*F)|\b\d+\b(?!.*\.$)

演示

在python中,我会很容易地这样做,

import re
string = """cat 123 sat.
I like 000 not (456) though 111 is fine
222 if(  //endif if(cat==789 stuff  //endif   333"""
for line in string.split('\n'):                                  # Split the input according to the `\n` character and then iterate over the parts.
    if not line.endswith('.'):                                   # Don't consider the part which ends with a dot.
        for i in re.split(r'\([^()]*\)|if\(.*?//endif', line):   # Again split the part by brackets or if condition which endswith `//endif` and then iterate over the inner parts.
            for j in re.findall(r'\b\d+\b', i):                  # Then find all the numbers which are present inside the inner parts and then loop through the fetched numbers.
                print(j)                                         # Prints the number one ny one.

输出:

000
111
222
333
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.