背景
我继承了大量XML文件,这些文件始终包含一个带有两个开始的标记,而不是一个开始和一个结束。我需要遍历所有这些文件并更正格式错误的XML。
下面是一个不好的XML的简化示例,它是每个文件中完全相同的标记:
<meals>
<breakfast>
Eggs and Toast
</breakfast>
<lunch>
Salad and soup
<lunch>
<supper>
Roast beef and potatoes
</supper>
</meals>
注意,<lunch>
标记没有闭包。这在所有文件中都是一致的。
问题
使用C#的regex
来解决这个问题是最好的吗?如果是这样的话,我该怎么做呢?
我已经知道如何迭代文件系统并将文档读入XML或string对象,因此您不需要回答这一部分。
谢谢!
发布于 2012-04-06 03:58:06
我认为,如果情况真的像你所描述的那样简单,那么正则表达式就有点过分了(例如,它总是相同的标签,而它们总是只有一个)。如果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-06 03:57:43
如果损坏的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属性。在该分组中,还有另一个命名组-这个组使用名称" tag“并捕获标记名称本身,没有尖括号。
然后,正则表达式懒惰地捕获一堆中间文本((.+?)
),然后捕获另一个“打开”标记,该标记使用反向引用指定。惰性捕获在那里,所以它不会吞噬文本中任何可能插入的开始标记。
因为XML可能跨越多个换行符,所以需要RegexOptions.Singleline
。
然后,该逻辑在循环中应用此正则表达式,将任何匹配的文本替换为具有结束标记的固定版本有效的xml。固定的XML是通过简单的字符串切片生成的。
在以下情况下,此正则表达式将不起作用:
...but这种方法仍然有效。你只需要稍微调整一下。
发布于 2012-04-06 04:08:07
如果您的xml文件中唯一的问题是您所显示的内容,那么Chesso的答案应该可以满足需要。事实上,我会走这条路,即使它完全满足了我80-90%的需求-其余的情况下,我可能会选择手动处理或编写特定的处理代码。
说,如果文件结构是复杂的,而不是你所描述的简单,那么你可能应该看看一些文本词法分析器,它将允许你将你的文件内容分解成标记。检查和纠正不规则性的标记语义分析必须由您完成,但至少解析文本会简单得多。请参阅下面几个链接到C#中词法分析的资源:
https://stackoverflow.com/questions/10038722
复制