是什么导致这个正则表达式匹配一切?

时间:2016-05-13 17:03:37

标签: regex

我正在尝试使用此正则表达式:

^(\s+)<ProjectReference(.|\s)+?(Project2)</Name>(.|\s)+?</ProjectReference>

...仅查找此部分:

    <ProjectReference Include="..\..\Project2\Project2.csproj">
      <Project>{6c2a7631-8b47-4ae9-a68f-f728666105b9}</Project>
      <Name>Project2</Name>
    </ProjectReference>

...在下面的文件中:

what is causing this text up here to be selected??

    <ProjectReference Include="..\..\Project1\Project1\Project1.csproj">
      <Project>{714c6b26-c609-40a4-80a9-421bd842562d}</Project>
      <Name>Project1</Name>
    </ProjectReference>


  <ItemGroup>
    <ProjectReference Include="..\..\Project2\Project2.csproj">
      <Project>{6c2a7631-8b47-4ae9-a68f-f728666105b9}</Project>
      <Name>Project2</Name>
    </ProjectReference>
    <ProjectReference Include="..\..\Project3\Project3\Project3.csproj">
      <Project>{39860208-8146-429f-a1d1-5f8ed2fd7f5f}</Project>
      <Name>Project3</Name>
    </ProjectReference>
    <ProjectReference Include="..\..\Project4\Project4.csproj">
      <Project>{58144d60-19d9-4d11-8ae6-088e03ccf874}</Project>
      <Name>Project4</Name>
    </ProjectReference>
    <ProjectReference Include="..\..\Project5\Project5.csproj">
      <Project>{33baa509-ad24-4a72-a2fc-8f297e75e90d}</Project>
      <Name>Project5</Name>
    </ProjectReference>
  </ItemGroup>
  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>

在Notepad ++中,它似乎最初定位匹配,但随后它会在第二个匹配中匹配整个文档(因此它总共找到2个匹配)。当我的实用程序用空字符串替换项目文件的全部内容时,我最初在我的.NET应用程序中发现了这一点,有效地清除了整个内容。

我花了一个多小时辛苦劳作,所以让我们看看SE是否可以解决这个问题。

更新虽然我已经标记了一个实际有效的答案,但我最终还是采用了一种不那么神奇的方法来确保在以后的路上不会出现罕见的正则表达式怪癖就像最近的情况一样。

^(\s+)<ProjectReference.+?({0})\.(csproj|vbproj).*\r\n.*\r\n\s+<Name>{0}</Name>\r\n\s*</ProjectReference>

...其中{0}是我项目的名称。虽然更详细,但这种解决方案不太可能因过多匹配而出错。我在我的.NET应用程序中使用RegexOptions.Multiline,以便我可以锚定到一行的开头。

2 个答案:

答案 0 :(得分:3)

我认为最好的方法是使用 xpath表达式 xml解析器

但是,正如您在评论中所述,如果您想使用正则表达式捕获该特定部分,那么您可以使用此:

(<ProjectReference.*?Project2[\s\S]*?</ProjectReference>)

<强> Working demo

匹配信息

MATCH 1
1.  [209-384]   `<ProjectReference Include="..\..\Project2\Project2.csproj">
      <Project>{6c2a7631-8b47-4ae9-a68f-f728666105b9}</Project>
      <Name>Project2</Name>
    </ProjectReference>`

除了regex101还使用了SublimeText来显示它的工作情况,但Notepad ++的正则表达式引擎很差,并且通常会用[\s\S]*?这样的技巧搞砸了:

enter image description here

另一方面,与您关于&#34;为什么失败&#34;的问题相关,您的正则表达式不是失败,但您的模式允许greedy匹配(即使使用懒惰的算子)因为你的(.|\s)交替:

^(\s+)<ProjectReference(.|\s)+?(Project2)</Name>(.|\s)+?</ProjectReference>
                          ^--- HERE

如果您查看Regex101 explanation,则可以看到:

2nd Capturing group (.|\s)+?
  Quantifier: +? Between one and unlimited times, as few times as possible, expanding as needed [lazy]
  Note: A repeated capturing group will only capture the last iteration. Put a capturing group around the repeated group to capture all iterations or use a non-capturing group instead if you're not interested in the data
  1st Alternative: .
    . matches any character (except newline)
  2nd Alternative: \s
    \s match any white space character [\r\n\t\f ]

答案 1 :(得分:2)

首先,永远不要使用(.|\s)来匹配所有内容 - 包括换行符;它是一个等待发生的冻结(有关详细信息,请参阅this answer)。 Notepad ++中的搜索对话框包含一个用于此目的的复选框,标记为. matches newline

其次,无论如何,你不应该得到那个结果。我已经在Notepad ++的本地副本中复制了它,它看起来像一个bug。也许正则表达式 冻结,NPP无法捕获错误。无论如何,你应该只获得一场比赛,当我选择. matches newline并将你的正则表达式更改为此时会发生什么:

^\h*<ProjectReference.*?Project2</Name>.*?</ProjectReference>

但是,它仍然匹配太多,包含Project1Project2元素。这是因为非贪婪量词只会影响匹配结束的位置,而不会影响它开始的位置。您需要使用更具体的内容,以确保匹配不会超出其开始的元素。我认为这是最可靠的方法:

^\h*<ProjectReference(?:(?!</?ProjectReference).)*Project2</Name>.*?</ProjectReference>

这个想法是允许任何匹配字符(包括换行符),,除非它是序列<ProjectReference</ProjectReference的第一个字符。因此,一旦它开始匹配开始<ProjectReference>标记,它就可以匹配除了另一个这样的标记(打开或关闭)之外的任何内容,直到找到标记字符串(Project2)。

更新:这绝对是Notepad ++中的一个错误。我自己做了一些测试,并找到了其他报告来确认(herehere)。那些家伙在尝试触发错误时非常有创意,但归结为:如果正则表达式需要太长时间才能匹配,NPP会错误地选择所有内容。