【问题标题】:What's causing this regex to match everything?是什么导致这个正则表达式匹配所有内容?
【发布时间】:2016-05-13 17:03:36
【问题描述】:

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

^(\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,以便我可以锚定到行首。

【问题讨论】:

  • 这个(.|\r\n)+。贪婪的. 会捕获一切。
  • @BoristheSpider 糟糕,在撰写我的问题时编辑错误。我用? 纠正了它,但它仍然在做同样的事情。我直接从我的 Notepad++ 查找窗口中复制并粘贴了该正则表达式。
  • 您似乎想提取与project2相关的部分。为什么不使用 xpath 表达式或 xml 解析器?
  • @FedericoPiazza 我想我可以。我正在尝试用 DLL 引用替换项目引用,而正则表达式只是我能想到的第一种方法。我选择它是因为我对正则表达式很满意,而且不需要学习任何新东西。
  • @oscilatingcretin,好的。所以,要确保你只想要上面提到的与Project2\Project2.csproj 相关的特定部分?

标签: regex


【解决方案1】:

我认为最好的方法是使用 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]*? 之类的技巧来搞砸它:

另一方面,与您关于“为什么失败”的问题有关,您的正则表达式不是 failing 但您的模式允许 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 ]

【讨论】:

  • 您使用Include 元素中的Project2 作为标记,而OP 使用&lt;Name&gt; 元素中的标记。这使任务更简单,但你能确定它是有效的吗?
  • @AlanMoore,好眼力,没看到。我基于 OP 目标来获取它。让我们看看 OP 怎么说,也许使用 Include 元素作为哨兵很好。
  • @AlanMoore 您的解决方案适用于 Notepad++ 和我的 .NET 应用程序。我非常喜欢[\s\S] 的把戏。将来,我可能不会尝试所有这些正则表达式魔法,而只会采用更直接的方法,我将在我的问题结束时发布。
  • @oscilatingcretin 很高兴为您提供帮助。 [\s\S] 这是一个众所周知的技巧,可以在不使用 s 标志的情况下匹配所有内容。它通常用于像您这样. 与新行不匹配但[\s\S] 匹配的情况。
【解决方案2】:

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

其次,无论如何,你都不应该得到那个结果。我已经在 Notepad++ 的本地副本中复制了它,它看起来像一个错误。也许正则表达式 is 冻结,而 NPP 未能捕捉到错误。无论如何,你应该只得到一个匹配,当我选择 . matches newline 并将你的正则表达式更改为这样时会发生这种情况:

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

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

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

这个想法是允许点是任何匹配字符(包括换行符),除非它是序列&lt;ProjectReference&lt;/ProjectReference 的第一个字符。因此,一旦它开始匹配开头的&lt;ProjectReference&gt; 标记,它就可以匹配除另一个此类标记(开头或结尾)之外的任何内容,直到找到标记字符串(Project2)。

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

【讨论】:

  • 我在 Notepad++ 中尝试了你的第二个正则表达式,它可以工作,但我必须启用 . matches newline。这个问题最初是在我的 .NET 应用程序中发现的,所以我需要一个在那里工作的解决方案。 .NET 本机正则表达式选项仅支持 RegexOptions.Multiline,这与 Notepad++ 的选项不同。不过,我赞成你的回答。我找到了一个解决方案,它采用更直接的方法,而不是尝试做所有这些正则表达式巫术来匹配神奇的模式。我会尽快发布的
  • 抱歉,我认为 Notepad++ 是您的目标口味。在 .NET 中,您必须使用 Multiline 模式使 ^ 在行首匹配(NPP 在多行模式下始终),并使用 Singleline 使 . 匹配换行符。此外,.NET 不支持\h(水平空格),所以要么使用[ \t*],要么返回使用\s*。或者完全放弃它;除非您要规范化前导空格,否则这部分不是必需的。
  • .NET 中 SingleLine 模式的问题在于,根据我的测试,它实际上将整个字符串视为单行字符串,因此您不能使用 ^ 锚定到行在字符串的中间。
  • 我认为您一定是误解了您的测试结果。单行只影响点,多行只影响锚点(^$)。两者之间没有重叠,并且(尽管它们的名称暗示)它们不是相互排斥的。
猜你喜欢
  • 1970-01-01
  • 2021-10-22
  • 1970-01-01
  • 1970-01-01
  • 2011-04-13
  • 2018-08-24
  • 1970-01-01
  • 2013-04-19
  • 1970-01-01
相关资源
最近更新 更多