尝试解析XML时失控回溯

时间:2016-01-05 09:33:19

标签: regex xml openxml

我在尝试解析一些OpenXML标准(docx)时遇到问题。我们使用{Contact.MailAddress}之类的表达式,并在第二步中从数据填充此内容。但是,Word(和LibreOffice)的方式是,他们有时会像这样分割这些标签:

<w:r w:rsidRPr="00E22BCD">
    <w:rPr>
        <w:rFonts w:eastAsia="Times New Roman"/>
        <w:lang w:val="fr-CH"/>
    </w:rPr>
    <w:t>{</w:t>
</w:r>
<w:proofErr w:type="spellStart"/>
<w:r w:rsidRPr="00E22BCD">
    <w:rPr>
        <w:rFonts w:eastAsia="Times New Roman"/>
        <w:lang w:val="fr-CH"/>
    </w:rPr>
    <w:t>Contakt.MailAddress</w:t>
</w:r>
<w:proofErr w:type="spellEnd"/>
<w:r w:rsidRPr="00E22BCD">
    <w:rPr>
        <w:rFonts w:eastAsia="Times New Roman"/>
        <w:lang w:val="fr-CH"/>
    </w:rPr>
    <w:t>}</w:t>
</w:r>

所以我做了以下正则表达式:

(?<expr>{)((?<tag><[^>]+>)|(?<expr>[\w\s.]+))+(?<expr>})

expr组中的所有内容都是{Contact.MailAddress}表达式的一部分,并合并在一起。 tag组中的所有内容都会合并到标签中,以便稍后将xml固定在一起。

现在,这非常有效。但是,当我们使用{foreach}语法时,xml可能会变得非常大,然后我们就会失控。

任何人都可以想到一个正则表达式,它可以更好地捕获这个并不会导致失控吗?

编辑1:程序是用C#/ .NET编写的。对于正则表达的味道。

编辑2:我采取了另一种方法:我列出了匹配项{[^}]}的所有匹配项,并且在那里我用任何内容替换所有标记和空格:

var matches = Regex.Matches(xml, @"{[^}]+}")
    .Cast<Match>()
    .OrderByDescending(x => x.Index)
    .ToList();

foreach (var match in matches)
{
    var replacement = Regex.Replace(match.Value, @"<[^>]+>", "");
    replacement = Regex.Replace(replacement, @"\s+", "");
    xml = xml.Substring(0, match.Index) + replacement + xml.Substring(match.Index + match.Length);
}

诀窍是按索引降序对匹配进行排序,以便Substring中的数学运算正常。

1 个答案:

答案 0 :(得分:1)

您好像要删除{}之间的所有标记和空格。如果你不担心不应该匹配的其他大括号,这应该有效:

s = Regex.Replace(s, 
    @"(?<brace>{)\s*(?:<[^<>]+>\s*)*|\s*(?:<[^<>]+>\s*)*(?<brace>})", 
    @"${brace}");

为安全起见,您可能需要添加最近的实际标签(假设它们始终相同):

@"(?<brace>{)</w:t>\s*(?:<[^<>]+>\s*)*|\s*(?:<[^<>]+>\s*)*<w:t>(?<brace>})"

使用正则表达式,我得到这个结果:

<w:r w:rsidRPr="00E22BCD">
    <w:rPr>
        <w:rFonts w:eastAsia="Times New Roman"/>
        <w:lang w:val="fr-CH"/>
    </w:rPr>
    <w:t>{Contakt.MailAddress}</w:t>
</w:r>

......根本没有回溯。

编辑:

事实证明,在大括号内的点之前和之后也插入了标签。我的原始解决方案并不适用于此,所以这是一个两阶段的方法,找到大括号括起来的文本,并用删除了标签和空格的相同文本替换它:

s = Regex.Replace(s, 
    @"{[^{}]*}", 
    m => Regex.Replace(m.Value, @"\s*(?:<[^<>]+>\s*)*", ""));