• C#


    对于小型XML文件,利用XDocument和XMLDocument可以很方便进行读写(推荐XDocument),但问题是XDocument和XMLDocument是In-Memory类型的,随着文件大小的增大,内存消耗会越来越大,同时读写速度会降低。本文总结如下几种方式操作大型XML文件:

    读 - Read

    利用XmlReader或XmlTextReader流式加载、解析XML文件。

    static IEnumerable<XElement> XStreamingElementHelper(string uri, string FindKey)
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.IgnoreComments = true;
        settings.IgnoreWhitespace = true;
    
        using (XmlReader rd = XmlReader.Create(uri, settings))
        {
            rd.MoveToContent();
            while (!rd.EOF)
            {
                if(rd.NodeTyp == XElement && rd.Name == FindKey)
                {
                    XElement item = XElement.ReadFrom(rd) as XElement;
                    if (item != null)
                    {
                        yield return item;
                    }
                }
                else
                {
                    rd.Read();
                }
            }
        }
    }
    

    其中,内部用XElement操作。  

    写 - Write

    此部分开始前,先提供一个创建空的 xml 文档的代码

    string filePath = Directory.GetCurrentDirectory() + "/123456.xml";
    if (!File.Exists(filePath))
    {
        XmlDocument xdInit = new XmlDocument();
        StringBuilder sb = new StringBuilder();
        sb.Append("<?xml version="1.0" encoding="gb2312" ?><Root>");
        sb.Append("</Root>");
        string strXml = sb.ToString();
        xdInit.LoadXml(strXml);
        xdInit.Save(filePath);
    }
    

    该方法生成的 .xml 文档如下

    <?xml version="1.0" encoding="gb2312"?>
    <Root>
    </Root>
    

    或使用如下代码

    if (!System.IO.File.Exists(filePath))
    {
        System.IO.File.Create(filePath).Close();
        XDocument xdInit = new XDocument(
            new XElement("Root"));
        xdInit.Save(filePath);
        xdInit = null;
    }
    

    该方法生成的 .xml 文档如下

    <?xml version="1.0" encoding="utf-8"?>
    <Root />
    

    注意,以上2种格式等同,xml 文档默认的编码方式是 utf-8

    在拼接 xml 字符串时,要特别注意非法字符的转义处理:

    System.Security.SecurityElement.Escape(str)

    向已存在的大型XML文件中追加新结点,不能采用In-Memory的方式。推荐如下几种写方法:

    1. XStreamingElement

    问题引出:如何巨型xml文件

    使用示例

     1 // Filepath:带写.xml文件路径;FindKey:待查找结点名称
     2 // xeList:要新增的XElement结点列表
     3 string FindKey = "Task";
     4 var items = XStreamingElementHelper(Filepath, FindKey);
     5 items = items.Concat(xeList); 
     6 var xml = new XStreamingElement
     7     ("Root",
     8         from item in items
     9         select item
    10     );
    11 
    12 // 文件替换
    13 var newFile = Filepath + "TEMP";
    14 xml.Save(newFile);
    15 System.IO.File.Delete(Filepath);
    16 System.IO.File.Move(newFile, Filepath);
    17 
    18 GC.Collect();
    XStreamingElement实现写文件写

    参考

    XStreamingElement类 - msdn

    How to: Perform Streaming Transform of Large XML Documents (C#) - msdn

    关于XStreamingElement vs XElement;  Linq to Xml:XStreamingElement

    2. XML文件包含方法

    利用包含文件 .txt 和 .xml实现大型XML文件的有效追加操作以及修改操作:

    • .xml:格式正确的标准XML文件
    • .txt:XML片段

    格式正确的XML文件

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE Root [<!ENTITY events SYSTEM "people.txt">]>
    <Root>&events;</Root>
    

    上面代码中,必须是 &,而不能是 &amp;

    示例代码

     1 string fileName = "sqh";
     2 string fileDir = Directory.GetCurrentDirectory() + "/";
     3 string xmlFilepath = fileDir + fileName + ".xml";
     4 string txtFilepath = fileDir + fileName + ".txt";
     5 if(!File.Exists(xmlFilepath))
     6 {
     7     // 创建.txt文件
     8     File.Create(txtFilepath).Close();
     9     
    10     // 创建.xml文件
    11     string type = string.Format("<!ENTITY events SYSTEM "{0}.txt">", fileName);
    12     XDocument XDoc = new XDocument(
    13         new XDocumentType("Root", null, null, type),
    14         new XElement("Root", "&events;")
    15         );
    16     XDoc.Save(xmlFilepath);
    17 }
    18 else
    19 {
    20     StreamWriter sw = File.AppendText(txtFilepath);
    21     XmlTextWriter xtw = new XmlTextWriter(sw);
    22     xtw.Formatting = Formatting.Indented;  // 缩进格式
    23     xtw.Indentation = 2;
    24     
    25     // 新增结点
    26     xtw.WriteStartElement("event");
    27     xtw.WriteElementString("key", "value");
    28     xtw.WriteEndElement();
    29     
    30     xtw.WriteWhitespace(Environment.NewLine); // 换行,必须带
    31     xtw.Close();
    32 }
    XML = .xml + .txt

    上面代码创建的XML文件,& 会被转义成 &amp; 是错误的,使用下面的创建方式

    XmlDocument xd = new XmlDocument();
    string head = "<?xml version="1.0" encoding="utf-8"?>";
    string docType = string.Format("<!DOCTYPE Root [<!ENTITY Persons SYSTEM "{0}.txt">]>", txtFileName);
    string rootBody = "<Root>&Persons;</Root>";
    StringBuilder sb = new StringBuilder();
    sb.Append(head); sb.Append(docType); sb.Append(body);
    string strXml = sb.ToString();
    xd.LoadXml(strXml);
    xd.Save(filePath);
    

    注意,每写一次.txt文件,必须添加如下代码,否则会出现结点粘结的情况

    xtw.WriteWhitespace(Environment.NewLine);

    此处可以使用 StreamWriter,亦可使用 FileStream,代码如下

    FileStream filestream = new FileStream(FileName, FileMode.Append);
    XmlTextWriter xtw = new XmlTextWriter(filestream, Encoding.Default);
    xtw.Formatting = Formatting.Indented;
    xtw.Indentation = 2;
    ... ...
    xtw.Close();
    filestream.Close();
    

    若使用该 .txt + .xml 方法,应采用如下方式读取 .xml 文件

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.DtdProcessing = DtdProcessing.Parse;  // 必须的
    settings.ValidationType = ValidationType.None; // 可选的
    settings.IgnoreComments = true;
    //settings.IgnoreWhitespace = true;  // 视while{}中代码情况
    XmlReader rd = XmlReader.Create(filepath, settings);
    
    while (rd.Read())
    {
        if (rd.NodeType == XmlNodeType.Element && rd.Name == "person")
        { ... }   
    }
    

    注意,因为要使用 XmlReaderSettings 设置读取配置,因此只能使用 XmlReader,而不能使用如下代码

    XmlTextReader rd = new XmlTextReader(filepath);
    

    参考修改大型 XML 文件的有效方法 - MSDN

    3. log4net

    引用:using log4net.dll

    问题引出:如何log4net to xml?

    如何使用:Write a class Deriving from XmlLayoutBase, override the FormatXml method and instruct your appender to use. 

    4. 文件拆分

    xml/txt 写文件时,文件会越来越大,可以设置时间点或者文件大小限制,将大文件拆分成多个小文件。

    // 文件大小
    FileInfo fileInfo = new FileInfo(FilePath);
    double fileSize = System.Math.Ceiling(fileInfo.Length / 1024.0);
    

    该方法可以利用 log4net 实现,具体可参考:log4net - sqh

    参考

    关于XML格式化的问题xml日志乱码的问题

    如何使用log4net写XML

  • 相关阅读:
    【转】CDH rpm+http方式离线部署
    处理CDH环境Hadoop:NameNode is not formatted
    使用 prometheus-operator 监控 Kubernetes 集群【转】
    Prometheus监控k8s(10)-PrometheusOperator-更优雅的Prometheus部署【转】
    kali 网络配置作死踩坑
    01 校招信息收集渠道
    Pypora打开markdown(md)文件保存为PDF文件
    Github上的md文件查看后下载PDF文档方法(将HTML文件保存为PDF格式)
    利用后裔采集器快速采集文本数据(以京东为例)
    office安装公式编辑器mathtype6.9及mathtype过期解决方案
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/6476715.html
Copyright © 2020-2023  润新知