如何使用正则表达式匹配多行模式中间包含特定文本的文本?

时间:2012-04-25 00:24:43

标签: c# regex expression multiline lookahead

我正在尝试创建一个C#正则表达式,它检测我们的.csproj文件中的引用何时没有< SpecificVersion>设置为False(必须在所有< s之后添加一个空格以使其在StackOverflow中正确显示)。所以这些是我需要处理的案例:

1. <Reference Include="IQ.MyStuff1, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL" />
2. <Reference Include="IQ.MyStuff2, Version=4.7.22.21777, Culture=neutral, processorArchitecture=MSIL">
    <HintPath>..\..\DebugDLLFiles\IQ.MyStuff2.dll</HintPath>
</Reference>
3. <Reference Include="IQ.MyStuff3, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">
    <HintPath>..\..\DebugDLLFiles\IQ.MyStuff3.dll</HintPath>
    <SpecificVersion>True</SpecificVersion>
</Reference>
4. <Reference Include="IQ.MyStuff4, Version=4.5.3.17401, Culture=neutral, processorArchitecture=MSIL">
    <SpecificVersion>True</SpecificVersion>
</Reference>

所以基本上任何没有明确具有“&lt; SpecificVersion&gt; False&lt; / SpecificVersion&gt;”的文件引用在它。

所以让我们忽略第一种情况,因为它没有像其他3那样的身体,可以区别对待。所以这就是我到目前为止所做的:

<Reference(\s|\n|\r)*?  # Match against '<Reference '.
Include=""IQ\..*?""     # Match against the entire Include attribute; We only care about IQ DLLs.
(\s|\n\r)*?>            # Eat any whitespace and match against the closing tag character.
[What should go here?]
</Reference>            # Match against the closing tag.

所以我在[应该去哪里?]块中尝试了很多东西,但似乎无法让任何工作完全正常。我最接近的是使用以下块:

(?!                     # Do a negative look-ahead to NOT match against this Reference tag if it already has <SpecificVersion>False</SpecificVersion>.
    (.|\n|\r)*?         # Eat everything before the <SpecificVersion> tag, if it even exists.
    <SpecificVersion>(\s|\n|\r)*?False(\s|\n|\r)*?</SpecificVersion>    # Specify that we don't want to match if this tag already has <SpecificVersion>False</SpecificVersion>.
)
(.|\n|\r)*?             # Eat everything after the <SpecificVersion> tag, if it even existed.

这适用于所有情况,除了在我想要匹配的任何一个下面有有效引用的地方,其中有效的引用看起来像:

<Reference Include=\"IQ.MyStuff5, Version=4.5.3.17401, Culture=neutral, processorArchitecture=MSIL\">
    <SpecificVersion>False</SpecificVersion>
</Reference>

我正在使用的预测似乎不会停留在&lt; /参考和GT;标记,但继续向下看整个文件,以确保其下面的文字没有“&lt; SpecificVersion&gt; False&lt; / SpecificVersion&gt;”。

如何在第一个“&lt; / Reference&gt;”中使我的前瞻停止它会遇到,或者如果你有另一种方法来解决我的问题,我也会对此持开放态度。任何建议表示赞赏。感谢。

3 个答案:

答案 0 :(得分:3)

放弃正则表达式。它注定要失败。不是XML吗?为什么不这样对待呢?

don't parse HTML with regex”规则同样适用于XML。

答案 1 :(得分:2)

如果你想尝试使用正则表达式,我会建议这样的事情:

<Reference[^>]*?>(?:.(?!</Reference>))*?<SpecificVersion>([^<]*?)</SpecificVersion>

它匹配所有标签内的标签 - 即它将完全忽略任何没有标签的参考标签。

  • 查找参考标签
  • 匹配所有非关闭参考标记的内容,直到找到标记为
  • 然后它捕获标签内的值

我在regexpal中在线测试它似乎在多种情况下正常工作。

编辑:

  • 使用RegexOptions.Singleline使点匹配新行

如果你想匹配完全不存在SpecificVersion标签的情况,请尝试进行此更改 - 它将尝试使用标签匹配选项,但如果失败,它仍将匹配

<Reference[^>]*?>(?:.(?!</Reference>))*?(<SpecificVersion>([^<]*?)</SpecificVersion>)|<Reference[^>]*?>(?:.(?!</Reference>))*?(?:<SpecificVersion>([^<]*?)</SpecificVersion>)?

让我知道你是如何上场的。

答案 2 :(得分:0)

因此,根据花花公子的建议,我研究了正则表达式的替代品。我发现了Linq To XML,它很容易解决我的问题。这是我用来解决问题的代码。它查找.csproj文件中的所有引用到IQ DLL文件,并确保它们都具有&lt; SpecificVersion&GT;假&LT; / SpecificVersion&GT;元件。仅仅为了一些背景信息,我需要这样做的原因是当特定版本设置为True时,我们的构建在我们的本地机器上运行良好,但它在我们的TFS构建服务器上中断,除非它被设置为False。我很确定这样做的原因是我们的TFS构建更新了版本号,因此每个项目设置使用的版本都是过时的。无论如何,这是代码:)

// Let's parse us some XML!
XElement xmlFile = XElement.Load(filePath);

// Grab all of the references to DLL files.
var iqReferences = xmlFile.Descendants().Where(e => e.Name.LocalName.Equals("Reference", StringComparison.InvariantCultureIgnoreCase));

// We only care about iQ DLL files.
iqReferences = iqReferences.Where(r => r.Attribute("Include") != null && r.Attribute("Include").Value.StartsWith("IQ.", StringComparison.InvariantCultureIgnoreCase));

// If this project file doesn't reference any iQ DLL files, move on to the next project file.
if (!iqReferences.Any())
    continue;

// Make sure they all have <SpecificVersion> set to False.
foreach (XElement reference in iqReferences)
{
    // If this Reference element already has a child SpecificVersion element whose value is false, skip this reference since it is good.
    if (reference.Elements().Where(e => e.Name.LocalName.Equals("SpecificVersion", StringComparison.InvariantCultureIgnoreCase))
        .Any(e => e.Value.Equals("False", StringComparison.InvariantCultureIgnoreCase)))
        continue;

    // Add this reference to the list of bad references.
    badReferences.AppendLine("\t" + reference.Attribute("Include").Value);

    // Fix the reference.
    reference.SetElementValue(reference.Name.Namespace + "SpecificVersion", "False");
}