尝试解析时失控回溯 XML
Runaway Backtracking when trying to parse XML
我在尝试解析某些 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
中的数学运算有效。
您似乎想删除 {
和 }
之间的所有标记和空格。如果您不担心其他不应该匹配的括号,这应该有效:
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>
...而且根本没有回溯。
编辑:
事实证明,标签也被插入到大括号内的点之前和之后。我原来的解决方案对此不起作用,所以这里有一个 two-stage 方法,它可以找到 brace-enclosed 文本并将其替换为相同的文本并删除标签和空格:
s = Regex.Replace(s,
@"{[^{}]*}",
m => Regex.Replace(m.Value, @"\s*(?:<[^<>]+>\s*)*", ""));
我在尝试解析某些 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
中的数学运算有效。
您似乎想删除 {
和 }
之间的所有标记和空格。如果您不担心其他不应该匹配的括号,这应该有效:
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>
...而且根本没有回溯。
编辑:
事实证明,标签也被插入到大括号内的点之前和之后。我原来的解决方案对此不起作用,所以这里有一个 two-stage 方法,它可以找到 brace-enclosed 文本并将其替换为相同的文本并删除标签和空格:
s = Regex.Replace(s,
@"{[^{}]*}",
m => Regex.Replace(m.Value, @"\s*(?:<[^<>]+>\s*)*", ""));