我继承了一大堆XML文件,这些文件始终包含一个带有两个开口的标签,而不是一个开口和一个闭包。我需要遍历所有这些文件并纠正格式错误的XML。
以下是错误XML的简化示例,它是每个文件中完全相同的标记:
<meals>
<breakfast>
Eggs and Toast
</breakfast>
<lunch>
Salad and soup
<lunch>
<supper>
Roast beef and potatoes
</supper>
</meals>
请注意<lunch>
标记没有关闭。这在所有文件中都是一致的。
最好使用regex
来修复此问题,如果是这样,我该怎么做?
我已经知道如何迭代文件系统并将文档读入XML或字符串对象,因此您无需回答该部分。
谢谢!
答案 0 :(得分: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>
表示由封闭的parens捕获的组可以通过名称“open”访问。该分组捕获开始标记,包括尖括号。它假设开始标记上没有xml属性。在该分组中,还有另一个命名组 - 这个名称使用名称“tag”并捕获标记名称本身,不带尖括号。
然后正则表达式懒洋洋地捕获一堆介入文本((.+?)
),然后另一个“开放”标记,用反向引用指定。懒惰捕获就在那里,因此它不会在文本中篡改任何可能的插入开放标记。
由于XML可能跨越多个换行符,因此您需要RegexOptions.Singleline
。
逻辑然后在循环中应用此正则表达式,将任何匹配的文本替换为固定版本 - 带有结束标记的有效xml。使用简单的字符串切片生成固定的XML。
如果符合以下条件,此正则表达式将无效:
......但这种方法仍然有效。你只需稍微调整一下。
答案 1 :(得分: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 );
答案 2 :(得分:0)
如果您的xml文件中唯一的问题是您所显示的内容,那么Chesso的答案就足够了。事实上,即使它完全满足我80-90%的需求,我也会走这条路 - 其余的情况下,我可以选择手动处理或编写特定的处理代码。
说,如果文件结构很复杂并且不像你描述的那么简单,那么你应该看看一些文本词法分析器,它允许你将文件内容分解为标记。用于检查和纠正不规则性的令牌的语义分析必须由您完成,但至少解析文本会更简单。请参阅以下几个链接到C#中的lexing的资源:
答案 3 :(得分:-1)
最好不要将这些视为XML文件:它们是非XML文件。这会立即告诉您,为处理XML而设计的工具将毫无用处,因为输入不是XML。您需要使用基于文本的工具。在UNIX上,这将是sed / awk / perl;我不知道Windows上的等价物是什么。