標籤:
一、上篇回顧
上兩篇都介紹了Linq to Xml的相關用法,需要注意的一點是Linq to Xml是in-memory的處理方式,所以有多少節點,就要消耗多少記憶體,如果這個Xml很大,但系統記憶體卻有限的情況下該怎麼辦呢?
下面我們就來一步步分析。
二、設定與實現目標
今天要做的是把某目錄下的所有檔案和目錄輸出到一個Xml裡面去,例如:
<?xml version="1.0" encoding="gb2312"?><folder name="bin"> <folder name="Debug"> <file name="ConsoleApplication6.exe" /> <file name="ConsoleApplication6.exe.config" /> <file name="ConsoleApplication6.pdb" /> <file name="ConsoleApplication6.vshost.exe" /> <file name="ConsoleApplication6.vshost.exe.config" /> <file name="ConsoleApplication6.vshost.exe.manifest" /> </folder> <folder name="Release"> <file name="ConsoleApplication6.exe" /> <file name="ConsoleApplication6.exe.config" /> <file name="ConsoleApplication6.pdb" /> <file name="ConsoleApplication6.vshost.exe" /> <file name="ConsoleApplication6.vshost.exe.config" /> <file name="ConsoleApplication6.vshost.exe.manifest" /> </folder></folder>
1. 使用Linq to Xml嘗試
如果使用之前介紹的Linq to Xml可以很快寫出一下代碼:
static void Main(string[] args){ var di = new DirectoryInfo("E:\\PDF"); XDocument doc = new XDocument(GetFolderContent(di)); doc.Save(Console.Out); Console.ReadKey();}private static XElement GetFolderContent(DirectoryInfo di){ return new XElement("folder", new XAttribute("name", di.Name), from subDir in di.GetDirectories() select GetFolderContent(subDir), from file in di.GetFiles() select new XElement("file", new XAttribute("name", file.Name)) );}
但是要注意一點,Linq to Xml是in-memory的方式工作的,也就是如果有1000級的目錄嵌套,沒級有100個子目錄,那麼久有100^1000個XElement在記憶體中建立出來。好吧,這麼計算一下,整個過程需要多少記憶體吧,10的2000次方*每一個XElement消耗的記憶體,就算只有1Byte,1G記憶體也只能處理10的30次方,所以要處理完這個情境,需要多少記憶體啊!可以說是不可能達到的。
2. 分析
上面的實現得益於Linq to Xml Api的簡易,但是卻受制於Linq to Xml的in-memory模型,要是有一種既可以受益於Linq to Xml的簡易,又可以使用非in-memory的模型就兩全其美了。
天下有這麼好的事情嗎?先不要急於下定論,讓我們來查查msdn吧:如何:執行大型 XML 文檔的流式轉換。
這裡提到了一個XStreamingElement的類,僅僅從名稱上,就可以猜到,這個類是一個類似XElement的類型,但是它又是一個類似Stream的類,並不是in-memory的,當然具體怎麼說還要看msdn。
msdn中有明確說明:表示支援延遲流輸出的XML樹中的元素。
而且備忘中說到:如果從輸入源(和文字檔)進行串流,則可以讀取非常大的文字檔,並產生非常大的XML文檔,同事保持較小的記憶體需求量。
也就是明確了XStremingElement的這個類本身就是(或類似)串流的,並不像普通的XElement的in-memory的處理方式。
3. 實現
找到了XStreamingElement這個Linq to Xml的另類API後,就可以實現前面的目標了。
XStreamingElement的用法與XElement十分相似,只需要把前面的方法稍作修改即可:
private static XStreamingElement GetFolderContent(DirectoryInfo di){ return new XStreamingElement("folder", new XAttribute("name", di.Name), from subDir in di.GetDirectories() select GetFolderContent(subDir), from file in di.GetFiles() select new XElement("file", new XAttribute("name", file.Name)) );}
不過需要額外修改一下外面的調用方式:
var di = new DirectoryInfo("d:\\sourcecode");GetFolderContent(di).Save(Console.Out);
注意:這裡必須要用XStreamingElement的Save方法,否則延遲求解的特性可能會失敗。
三、總結
本篇介紹了Linq to Xml中的異類:XStreamingElement,既得益於Linq to Xml的簡易,又擁有streaming的小記憶體特性,在操作超大Xml時這兩個特性可以讓工作事半功倍。
C#操作XML(四)