将非常大的文件从 xml 转换为 csv
Converting very large files from xml to csv
目前,我正在使用以下代码片段将包含 XML
数据的 .txt
文件转换为 .CSV
格式。我的问题是,目前这适用于大约 100-200 mb 的文件并且转换时间非常短(最多 1-2 分钟),但是我现在需要它来处理更大的文件(每个 1-2 GB)文件)。目前该程序会冻结计算机,使用此功能转换大约需要 30-40 分钟。不确定我将如何继续更改此功能。任何帮助将不胜感激!
string all_lines = File.ReadAllText(p);
all_lines = "<Root>" + all_lines + "</Root>";
XmlDocument doc_all = new XmlDocument();
doc_all.LoadXml(all_lines);
StreamWriter write_all = new StreamWriter(FILENAME1);
XmlNodeList rows_all = doc_all.GetElementsByTagName("XML");
foreach (XmlNode rowtemp in rows_all)
{
List<string> children_all = new List<string>();
foreach (XmlNode childtemp in rowtemp.ChildNodes)
{
children_all.Add(Regex.Replace(childtemp.InnerText, "\s+", " "));
}
write_all.WriteLine(string.Join(",", children_all.ToArray()));
}
write_all.Flush();
write_all.Close();
示例输入::
<XML><DSTATUS>1,4,7,,5</DSTATUS><EVENT> hello,there,my,name,is,jack,</EVENT>
last,name,missing,above <ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG> </XML>
<XML><DSTATUS>1,5,7,,3</DSTATUS><EVENT>hello,there,my,name,is,mary,jane</EVENT>
last,name,not,missing,above<ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG></XML>
示例输出::
1,4,7,,5,hello,there,my,name,is,jack,,last,name,missing,above,3,6,7,,8,4
1,5,7,,3,hello,there,my,name,is,mary,jane,last,name,not,missing,above,3,6,7,,8,4
有两种变体。首先是隐藏程序冻结,为此使用BackgroundWorker。
第二:逐个字符串读取文本文件,使用任何 Reader(Xml 或任何 text\file)。
您可以组合这些变体。
由于File.ReadAllText(p);
它冻结了
不要将完整的文件读入内存。 (这将首先开始交换,然后停止你的 CPU 因为没有更多的内存可用)
使用分块方法:逐行读取,逐行转换,逐行写入。
使用较低的级别XMLReaderclass,而不是XmlDocument
您需要采用流式处理方法,因为您当前正在将整个 2Gb 文件读入内存,然后对其进行处理。您应该读一点 XML,写一点 CSV 并继续这样做,直到您处理完所有内容。
可能的解决方案如下:
using (var writer = new StreamWriter(FILENAME1))
{
foreach (var element in StreamElements(r, "XML"))
{
var values = element.DescendantNodes()
.OfType<XText>()
.Select(e => Regex.Replace(e.Value, "\s+", " "));
var line = string.Join(",", values);
writer.WriteLine(line);
}
}
其中 StreamElements
的灵感来自 Jon Skeet 在对 this question 的回答中从 XmlReader
流式传输 XElement
的内容。我做了一些更改以支持您的 'invalid' XML(因为您没有根元素):
private static IEnumerable<XElement> StreamElements(string fileName, string elementName)
{
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (XmlReader reader = XmlReader.Create(fileName, settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == elementName)
{
var el = XNode.ReadFrom(reader) as XElement;
if (el != null)
{
yield return el;
}
}
}
}
}
}
如果您准备考虑一种完全不同的方法,请下载 Saxon-EE 9.6,获得评估许可证,然后运行以下流式 XSLT 3.0 代码:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="main">
<xsl:stream href="input.xml">
<xsl:for-each select="*/*">
<xsl:value-of select="*!normalize-space()" separator=","/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>
目前,我正在使用以下代码片段将包含 XML
数据的 .txt
文件转换为 .CSV
格式。我的问题是,目前这适用于大约 100-200 mb 的文件并且转换时间非常短(最多 1-2 分钟),但是我现在需要它来处理更大的文件(每个 1-2 GB)文件)。目前该程序会冻结计算机,使用此功能转换大约需要 30-40 分钟。不确定我将如何继续更改此功能。任何帮助将不胜感激!
string all_lines = File.ReadAllText(p);
all_lines = "<Root>" + all_lines + "</Root>";
XmlDocument doc_all = new XmlDocument();
doc_all.LoadXml(all_lines);
StreamWriter write_all = new StreamWriter(FILENAME1);
XmlNodeList rows_all = doc_all.GetElementsByTagName("XML");
foreach (XmlNode rowtemp in rows_all)
{
List<string> children_all = new List<string>();
foreach (XmlNode childtemp in rowtemp.ChildNodes)
{
children_all.Add(Regex.Replace(childtemp.InnerText, "\s+", " "));
}
write_all.WriteLine(string.Join(",", children_all.ToArray()));
}
write_all.Flush();
write_all.Close();
示例输入::
<XML><DSTATUS>1,4,7,,5</DSTATUS><EVENT> hello,there,my,name,is,jack,</EVENT>
last,name,missing,above <ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG> </XML>
<XML><DSTATUS>1,5,7,,3</DSTATUS><EVENT>hello,there,my,name,is,mary,jane</EVENT>
last,name,not,missing,above<ANOTHERTAG>3,6,7,,8,4</ANOTHERTAG></XML>
示例输出::
1,4,7,,5,hello,there,my,name,is,jack,,last,name,missing,above,3,6,7,,8,4
1,5,7,,3,hello,there,my,name,is,mary,jane,last,name,not,missing,above,3,6,7,,8,4
有两种变体。首先是隐藏程序冻结,为此使用BackgroundWorker。 第二:逐个字符串读取文本文件,使用任何 Reader(Xml 或任何 text\file)。 您可以组合这些变体。
由于File.ReadAllText(p);
不要将完整的文件读入内存。 (这将首先开始交换,然后停止你的 CPU 因为没有更多的内存可用)
使用分块方法:逐行读取,逐行转换,逐行写入。
使用较低的级别XMLReaderclass,而不是XmlDocument
您需要采用流式处理方法,因为您当前正在将整个 2Gb 文件读入内存,然后对其进行处理。您应该读一点 XML,写一点 CSV 并继续这样做,直到您处理完所有内容。
可能的解决方案如下:
using (var writer = new StreamWriter(FILENAME1))
{
foreach (var element in StreamElements(r, "XML"))
{
var values = element.DescendantNodes()
.OfType<XText>()
.Select(e => Regex.Replace(e.Value, "\s+", " "));
var line = string.Join(",", values);
writer.WriteLine(line);
}
}
其中 StreamElements
的灵感来自 Jon Skeet 在对 this question 的回答中从 XmlReader
流式传输 XElement
的内容。我做了一些更改以支持您的 'invalid' XML(因为您没有根元素):
private static IEnumerable<XElement> StreamElements(string fileName, string elementName)
{
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (XmlReader reader = XmlReader.Create(fileName, settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == elementName)
{
var el = XNode.ReadFrom(reader) as XElement;
if (el != null)
{
yield return el;
}
}
}
}
}
}
如果您准备考虑一种完全不同的方法,请下载 Saxon-EE 9.6,获得评估许可证,然后运行以下流式 XSLT 3.0 代码:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="main">
<xsl:stream href="input.xml">
<xsl:for-each select="*/*">
<xsl:value-of select="*!normalize-space()" separator=","/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>