3

背景

我继承了一组 XML 文件,这些文件始终包含一个带有两个开口而不是一个开口和一个闭包的标签。我需要遍历所有这些文件并更正格式错误的 XML。

这是错误 XML 的简化示例,它是每个文件中完全相同的标记:

<meals>
    <breakfast>
         Eggs and Toast
    </breakfast>
    <lunch>
         Salad and soup
    <lunch>
    <supper>
         Roast beef and potatoes
    </supper>
</meals>

请注意,<lunch>标签没有闭包。这在所有文件中都是一致的。

问题

最好使用regexC# 来解决这个问题,如果是这样,我将如何做到这一点?

我已经知道如何迭代文件系统并将文档读入 XML 或字符串对象,因此您无需回答该部分。

谢谢!

4

4 回答 4

3

如果您损坏的 XML 相对简单,如您在问题中所示,那么您可以摆脱一些简单的逻辑和基本的正则表达式。

    public static void Main(string[] args)
    {
        string broken = @"
<meals>
    <breakfast>
         Eggs and Toast
    </breakfast>
    <lunch>
         Salad and soup
    <lunch>
    <supper>
         Roast beef and potatoes
    </supper>
</meals>";

        var pattern1 = "(?<open><(?<tag>[a-z]+)>)([^<]+?)(\\k<open>)";
        var re1 = new Regex(pattern1, RegexOptions.Singleline);

        String work = broken;
        Match match = null;
        do
        {
            match = re1.Match(work);
            if (match.Success)
            {
                Console.WriteLine("Match at position {0}.", match.Index);
                var tag = match.Groups["tag"].ToString();

                Console.WriteLine("tag: {0}", tag.ToString());

                work = work.Substring(0, match.Index) +
                    match.Value.Substring(0, match.Value.Length - tag.Length -1) +
                    "/" +
                    work.Substring(match.Index + match.Value.Length - tag.Length -1);

                Console.WriteLine("fixed: {0}", work);
            }
        } while (match.Success);
    }

该正则表达式使用 .NET 正则表达式的“命名”捕获组功能。?<open>表示由封闭括号捕获的组将可以通过名称“open”访问。该分组捕获开始标签,包括尖括号。它假定开始标记上没有 xml 属性。在该分组中,还有另一个命名组 - 这个组使用名称“标签”并捕获标签名称本身,没有尖括号。

然后,正则表达式会延迟捕获一堆中间文本 ( (.+?)),然后是另一个“打开”标记,该标记由反向引用指定。懒惰的捕获就在那里,所以它不会在文本中吞食任何可能的干预打开标签。

由于 XML 可能跨越多个换行符,因此您需要RegexOptions.Singleline.

然后,该逻辑在循环中应用此正则表达式,将任何匹配的文本替换为固定版本 - 带有结束标记的有效 xml。固定的 XML 是通过简单的字符串切片生成的。

如果出现以下情况,此正则表达式将不起作用:

  • 开始标签上有 XML 属性
  • 有奇怪的间距 - 包含标签名称的尖括号之间的空格
  • 标签名称使用破折号或数字或任何非小写 ASCII 字符
  • 之间的字符串包括尖括号(在 CDATA 中)

...但这种方法仍然有效。你只需要稍微调整一下。

于 2012-04-06T03:57:43.203 回答
2

如果情况真的像您描述的那样简单,我认为正则表达式会有点矫枉过正(即,它总是相同的标签,而且总是只有一个标签)。如果您的 XML 文件相对较小(千字节,而不是兆字节),您可以将整个内容加载到内存中,使用字符串操作插入缺少的斜杠,然后就可以结束了。这将比尝试使用正则表达式更有效(更快)。如果您的文件非常大,您可以将其修改为逐行读取文件,直到找到第一个<lunch>标签,然后查找下一个并进行相应修改。这里有一些代码供您入门:

var xml = File.ReadAllText( @"C:\Path\To\NaughtyXml.xml" );

var firstLunchIdx = xml.IndexOf( "<lunch>" );
var secondLunchIdx = xml.IndexOf( "<lunch>", firstLunchIdx+1 );

var correctedXml = xml.Substring( 0, secondLunchIdx + 1 ) + "/" +
xml.Substring( secondLunchIdx + 1 );

File.WriteAllText( @"C:\Path\To\CorrectedXml.xml", correctedXml );
于 2012-04-06T03:58:05.523 回答
0

如果您的 xml 文件中的唯一问题是您所显示的内容,那么 Chesso 的答案应该足以满足需要。事实上,即使它完全满足了我 80-90% 的需求,我也会走这条路——其余的情况,我可能会选择手动处理或编写特定的处理代码。

就是说,如果文件结构很复杂并且不像您描述的那样简单,那么您可能应该查看一些文本词法分析器,它可以让您将文件内容分解为标记。标记的语义分析以检查和纠正违规行为必须由您完成,但至少解析文本会更简单。请参阅下面链接到 C# 中词法分析的一些资源:

  1. http://blogs.msdn.com/b/drew/archive/2009/12/31/a-simple-lexer-in-c-that-uses-regular-expressions.aspx
  2. 穷人的 C#“词法分析器”
  3. http://www.seclab.tuwien.ac.at/projects/cuplex/lex.htm
于 2012-04-06T04:08:06.723 回答
-2

最好避免将它们视为 XML 文件:它们是非 XML 文件。这立即告诉您,为处理 XML 设计的工具将毫无用处,因为输入不是 XML。您需要使用基于文本的工具。在 UNIX 上,这可能是 sed/awk/perl;我不知道 Windows 上的等价物是什么。

于 2012-04-06T09:45:17.927 回答