【问题标题】:Searching for specific text inside balanced chars (recursive在平衡字符内搜索特定文本(递归
【发布时间】:2018-04-23 19:04:17
【问题描述】:

鉴于以下(已清理)输入:

Return_t
func()
{
  Type<SubType> cursorFeature(true);

  while (nDist < 800)
  {
    Result = Example(&var, 0, cursorFeature); //interested in this because inside loop, and not dereferenced or incremented
    if (!(++cursorFeature).NoMoreRecords())
    {
      if (!BLAH(blah)
        && (otherFunc(&var, &cursorFeature->derefenced, MACRO) != 0))
      {
        bIsChanged = true;
        break;
      }
      memcpy(&var, &cursorFeature->dereferenced, sizeof(anotherType_t));
    }
  }

  //more stuff
  }
}

我有以下正则表达式,它捕获使用Type 后发生的循环

Type.*<.*>\s*(\w*)[^}]*?(?:while|for)\s*\(.*?\n?.*?(\{(?>[^{}]|(?-1))*\})

https://regex101.com/r/Kr0zQq/3

我还有以下正则表达式,用于捕获Type 类型变量的特定用途:

Type.*<.*>\s*(\w*)[\s\S]*?\K(?<!\+\+)\1(?!->|\+\+)

https://regex101.com/r/Kr0zQq/4

我的目标是以某种方式将这些组合起来,最好是 ONE 正则表达式(如果可能的话,我也希望能够在 VS 中进行此搜索)。鉴于递归正则表达式的性质,我不确定这是否可能......我怀疑它不是。如果没有,那么在搜索数百个文件时不会丢失文件名/数字上下文的聪明东西会很棒。我基本上需要文件名和行号。上下文很好,但不是必需的。

为了澄清,我想捕获cursorFeature,因为它的类型是Type,然后我想在"loop"{.....}中搜索它的使用

编辑

只是关于我使用正则表达式来解决这个问题的说明。正在搜索的代码超过一百万行,跨越由各种编译器编译并由多个构建系统构建的多个项目。并且同时使用宏和高级语言功能意味着,例如,即使 VS Intellisense 经常错误解析代码 VS 也能够编译。 YCM(vim)也是如此。所以,一个 70% 误报的可能过于贪婪的正则表达式是可以的。 (因为在循环中缺少变量使用的进一步发生,因为此时通常很容易扫描其余部分。)但是,尝试使用“通用”PCRE 作为单行执行此操作可能是愚蠢的。 :)

【问题讨论】:

  • 如果这个问题有什么不清楚的地方,我会全力以赴。我试图尽可能具体。
  • 有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。 -- Jamie Zawinski(署名)
  • 这里的问题是,正则表达式对于这项任务来说是完全错误的工具。您需要一个真正的解析器,而正则表达式将永远无法处理所有可能的情况。即使您让它工作,它也会中断其他输入,并且必须维护它的人,如果他们有任何经验,将把它扔掉并用解析器替换它。
  • 另一个想法。你不需要一个完整的解析器,只需要一个能够找到你需要的东西。不要使用单个正则表达式,编写 Perl 代码一次解析一行,这应该更容易。
  • 您最好尽可能避免使用贪婪的点量词。将此regex101.com/r/fxWtAF/2 与您的进行比较。

标签: c# regex visual-studio perl


【解决方案1】:

您有三个选项来查看匹配的变量名是否存在于后续循环中。第一个是将(\1) 添加到原子组并检查您的环境中是否存在此捕获组(如果可能的话):

(?>(\1)|[^{}]|(?-2))*

其次,您可以通过使用负前瞻来缓和[^{}] 的匹配过程:

(?>(?!\1)[^{}]|(?-1))*

但如果你没有像我在 cmets 中提供的演示中那样使右大括号成为可选,它会失败。

第三个更好的解决方法是使用动词(*ACCEPT),它会立即导致成功匹配结束,而无需对正则表达式进行进一步更改:

(?>(\1)(*ACCEPT)|[^{}]|(?-2))*

Live demo

【讨论】:

  • 非常感谢。不出所料,我得到了很多误报,但我得到了一组可管理的结果(大约一百个),结果发现了 2 个资源泄漏。几乎不是检测它们的理想方法,但它有效:)
  • 我的荣幸。这很正常。您必须发现更多案例才能应用更严格的规则,这些规则是我们无法通过一些随机猜测来考虑的。
【解决方案2】:

使用单个正则表达式执行此操作需要使用 Perl 代码块
和一个有条件的代码。

虽然接受的答案很好,但它无法验证类型变量
将存在于平衡循环内。

这里的代码只是按原样存储类型变量的位置
在所有嵌套循环中找到。它不会错过任何一个。

作为一个例子,我已经在一些位置播种了具有多个位置的数据集
循环,其他位置的单个位置,另一个位置没有。

在字符串中的位置之外,如何定位取决于您。

Perl

use strict;
use warnings;

$/ = undef;
my $data = <DATA>;

my @vals;

while ( $data =~ /

          (?{ @vals = () })             # Code block - Initialize @vals

          Type .* < .* > \s* 
          ( \w+ )                       # (1)
          [^}]*? 
          (?: while | for )
          \s* \( .*? 
          (?: \r? \n )?
          .*? 

          (?<core>                      # (2 start)
               \{
               (?>
                    (?<! \+\+ )
                    ( \1 )                        # (3)
                    (?! -> | \+\+ )
                    (?{
                         # Code block - Push capture position onto @vals
                         push (@vals, pos());
                    })
                 |  [^{}] 
                 |  (?&core) 
               )*
               \}
          )                             # (2 end)

          (?(?{
               # Code conditional - Fail this block (bypass) if didn't find any
               @vals == 0
            })
               (*SKIP) (*FAIL) 
          )
     /xg )
{
    print "\nfound   '$1'   at pos:   ";
    for ( @vals )
    {
        print $_ , ",  ";
    }
}

__DATA__


Return_t
func()
{
  Type<SubType> cursorFeature(true);

  while (nDist < 800)
  {
    Result1 = Example(&var, 0, cursorFeature);  
    if (!(++cursorFeature).NoMoreRecords())
    {
      if (!BLAH(blah)
        && (otherFunc(&var, &cursorFeature->derefenced, MACRO) != 0))
      {
        bIsChanged = true;
        Result2 = Example(&var, 0, cursorFeature);  
        break;
      }
      else
        Result3 = Example(&var, 0, cursorFeature);  

      memcpy(&var, &cursorFeature->dereferenced, sizeof(anotherType_t));
    }
  }

  //more stuff
  }

// ---------------------------------

Return_t
func()
{
  Type<SubType> globeFeature(true);

  while (nDist < 800)
  {
    if (!(++globeFeature).NoMoreRecords())
    {
      if (!BLAH(blah)
        && (otherFunc(&var, &penFeature->derefenced, MACRO) != 0))
      {
        bIsChanged = true;
        break;
      }
      else
      memcpy(&var, &globeFeature->dereferenced, sizeof(anotherType_t));
    }
  }
  }
} 

// ---------------------------------

Return_t
func()
{
  Type<SubType> penFeature(true);

  while (nDist < 800)
  {
    if (!(++penFeature).NoMoreRecords())
    {
      if (!BLAH(blah)
        && (otherFunc(&var, &penFeature->derefenced, MACRO) != 0))
      {
        bIsChanged = true;
        Result = Example(&var, 0, penFeature);  
        break;
      }
      else
      memcpy(&var, &penFeature->dereferenced, sizeof(anotherType_t));
    }
  }
  }
} 

输出

found   'cursorFeature'   at pos:   148,  378,  465,
found   'penFeature'   at pos:   1323,

【讨论】:

  • 这个答案需要我一点时间来处理。我认为您添加的 key 功能正在检测循环中变量使用的多个实例,我实际上并不需要(我很抱歉,因为我的问题没有说清楚)。我实际上在递归中使用了负前瞻\1,然后匹配\1,这似乎可以保证在循环中使用变量。 但是,嵌套的否定前瞻在我的解决方案(++-&gt;)中不起作用,导致许多误报。
  • 很好奇下次遇到类似问题的时候试试这个。我什至不知道可以在正则表达式中嵌入代码:)
【解决方案3】:

我的目标是以某种方式将这些组合起来,最好是一个正则表达式(如果可能的话,我也希望能够在 VS 中进行此搜索)。

这是一种在 VisualStudio IDE 中使用 Dot-Net 正则表达式的方法。

不会产生误报。
我认为 VS 将跨越输出窗口中的行,这将使您能够
查看代码中的块,必要时查看数千个文件。

我已经在 http://regexstorm.net/tester 进行了测试,效果很好。
无法创建永久链接,它说它太长了..

但是您可以在 regexstorm 自己尝试一下。

(如果您要在 C# 程序集中使用它,您可以获得所有(多个)Vars
在捕获集合的循环中找到:CaptureCollection cc = Match.Groups["Vars"].Captures;
然后遍历cc。)

这是压缩的

Type.*&lt;.*&gt;\s*(\w+(?!\w))[^}]*?(?:while|for)\s*\(.*?(?:\r?\n)?.*?(?&gt;\{(?&gt;(?&lt;!\+\+)(?&lt;Vars&gt;\1)(?!-&gt;|\+\+)|[^{}]|\{(?&lt;Depth&gt;)|\}(?&lt;-Depth&gt;))*(?(Depth)(?!))(?(Vars)|(?!))\})

可读版本

 Type .* < .* > \s* 
 (                             # Type variable to find in Loop
      \w+ 
      (?! \w )                      # Must be an entire word
 )
 [^}]*? 
 (?: while | for )             # While or for loop
 \s* \( .*? 
 (?: \r? \n )?
 .*? 

 (?>
      \{                            # Match opening {
      (?>                           # Then either match (possessively):
           (?<! \+\+ )                   # Not incrementing variable
           (?<Vars> \1 )                 # Collect the Variable                                                                                                   
           (?! -> | \+\+ )               # Not dereferencing or incrementing variable
        |                              # or
           [^{}]                         #   Anything (but only if we're not at the start of { or } )
        |                              # or
           \{                            #  { (and increase the braces counter)
           (?<Depth> )
        |                              # or
           \}                            #  } (and decrease the braces counter).
           (?<-Depth> )
      )*                            # Repeat as needed.
      (?(Depth)                     # Condition that the braces counter is at zero.
           (?!)                          # Fail if depth > 0
      )
      (?(Vars)                      # Condition that the Variables matched something
        |  (?!)                          # Fail if Vars stack is empty
      )
      \}                            # Then match a closing }. 
 )

祝你好运,让我们知道结果如何!

【讨论】:

  • 效果很好。比 perl 更容易快速测试,因为它需要一点(不多)重构来处理 find | xargsgit ls-files | xargs 管道。
  • @zzxyz - 我使用 VS2015 ide,而且我从来不需要在搜索中使用 dot-net 正则表达式。我很想看看输出必须跨行时会做什么。
  • 我希望 VS 中的搜索历史可以追溯到更远的地方,这样我就可以分享我所做的一些事情。我倾向于从 perl 开始然后有时切换的原因之一(如果我真的需要,我可以找到它)。我实际上做了一些疯狂的搜索和替换。但我对“有兴趣看到”有点困惑。你没有测试这个吗?还是你用 C# 测试过它?无论如何,IDE 只是突出显示/选择整个匹配项。
  • @zzxyz - 这个正则表达式我在在线 Dot-Net 测试器 regexstorm.com 上测试得非常好。我可以在 C# 中进行测试,但我确信它可以在那里工作。 VS ide 通常会在输出窗口中输出 1 行(实际上是 clistctrl 或 clistbox),当您双击时会将您带到文件中的 line 。我只是想知道当一个跨越行的单个匹配块时,所有行都在输出窗口中。每个匹配项通常以文件和行信息开头。下次我改商业代码的时候试试(丑陋的MFC)。
  • 哦,明白了……你已经说过了。是的,“查找全部”只会将您放到开始匹配的行中,并且当您双击时不会突出显示。 “查找下一个”实际上更适合这样的搜索
猜你喜欢
  • 1970-01-01
  • 2020-02-23
  • 2013-02-02
  • 2012-12-27
  • 1970-01-01
  • 2014-06-10
  • 2016-12-19
  • 2016-07-17
  • 1970-01-01
相关资源
最近更新 更多