分享
 
 
 

修改大型 XML 文件的有效方法

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

引言

随着 XML 成为大型信息源的常用表示格式,开发人员编辑大型 XML 文件时开始遇到问题。对于处理大型日志文件以及经常需要为这些文件追加信息的应用程序,尤其如此。编辑 XML 文件最直接的方法是,将其加载到 XmlDocument 中,在内存中修改文档,然后将其保存回磁盘。但是,这样做意味着要将整个 XML 文档加载到内存中,由于文档太大或应用程序需要的内存不够,这种方法可能会行不通。

这篇论文说明了修改 XML 文档的一些可供选择的方法,这些方法不涉及将文档加载到 XmlDocument 实例中的内容。

使用 XML 包含方法

建议的第一种方法对于向 XML 日志文件追加值最为有用。开发人员面临的常见问题是需要一种能够将新条目简单地追加到日志文件中而不用加载文档的方法。因为 XML 具有良好结构规则,所以使用传统方式(这种方法会因为日志文件格式不正确而结束日志文件)来向 XML 日志文件追加条目通常是非常困难的。

要说明的第一种方法是针对这样的情形,即目的是能够将条目快速地追加到 XML 文档中。这种方法包括创建两个文件。第一个文件是格式正确的 XML 文件,第二个是 XML 片段。格式正确的 XML 文件包括 XML 片段,XML 片段使用 DTD 中声明的 external entity 或者使用 xi:include element 。使用包含文件,通过在进行处理过程中简单地追加到 XML 文件,可以有效地更新文件包含 XML 片段的方法。包含文件和被包含文件的示例如下所示:

Logfile.xml:

<?xml version="1.0"?>

<!DOCTYPE logfile [

<!ENTITY events

SYSTEM "logfile-entries.txt">

]>

<logfile>

&events;

</logfile>

Logfile-events.txt:

<event>

<ip>127.0.0.1</ip>

<http_method>GET</http_method>

<file>index.html</file>

<date>2004-04-01T17:35:20.0656808-08:00</date>

</event>

<event>

<ip>127.0.0.1</ip>

<http_method>GET</http_method>

<file>stylesheet.css</file>

<date>2004-04-01T17:35:23.0656120-08:00</date>

<referrer>http://www.example.com/index.html</referrer>

</event>

<event>

<ip>127.0.0.1</ip>

<http_method>GET</http_method>

<file>logo.gif</file>

<date>2004-04-01T17:35:25.238220-08:00</date>

<referrer>http://www.example.com/index.html</referrer>

</event>

logfile-entries.txt 文件包括一个 XML 片段,并且可以使用典型的文件 IO 方法有效地进行更新。下面的代码说明了如何通过将条目追加到文本文件的结尾来将它添加到 XML 日志文件中。

using System;

using System.IO;

using System.Xml;

public class Test{

public static void Main(string[] args){

StreamWriter sw = File.AppendText("logfile-entries.txt");

XmlTextWriter xtw = new XmlTextWriter(sw);

xtw.WriteStartElement("event");

xtw.WriteElementString("ip", "192.168.0.1");

xtw.WriteElementString("http_method", "POST");

xtw.WriteElementString("file", "comments.aspx");

xtw.WriteElementString("date", "1999-05-05T19:25:13.238220-08:00");

xtw.Close();

}

}

一旦条目被追加到文本文件中,使用传统的 XML 处理方法,就可以处理 XML 日志文件中的条目。下面的代码使用 XPath 遍历了 logfile.xml 中的日志事件,同时列出了它们被访问时的文件以及被访问的文件。

using System;

using System.Xml;

public class Test2{

public static void Main(string[] args){

XmlValidatingReader vr =

new XmlValidatingReader(new XmlTextReader("logfile.xml"));

vr.ValidationType = ValidationType.None;

vr.EntityHandling = EntityHandling.ExpandEntities;

XmlDocument doc = new XmlDocument();

doc.Load(vr);

foreach(XmlElement element in doc.SelectNodes("//event")){

string file = element.ChildNodes[2].InnerText;

string date = element.ChildNodes[3].InnerText;

Console.WriteLine("{0} accessed at {1}", file, date);

}

}

}

上面的代码导致了下面的输出:

index.html accessed at 2004-04-01T17:35:20.0656808-08:00

stylesheet.css accessed at 2004-04-01T17:35:23.0656120-08:00

logo.gif accessed at 2004-04-01T17:35:25.238220-08:00

comments.aspx accessed at 1999-05-05T19:25:13.238220-08:00

更改 XmlReader 为 XmlWriter

在某些情况下,除了只将元素追加到根元素中外,还需要对 XML 文件执行更复杂的操作。例如,要筛选日志文件中的每一个条目,而这些条目在存档到日志文件前不符合某些特殊标准。要完成此任务的一种方法是将 XML 文件加载到 XmlDocument 中,然后通过 XPath 选择感兴趣的事件。但是,这样做涉及将整个文档加载到内存中,如果文档太大,则这种做法会受到限制。另一种选择方法为了这种任务会涉及使用 XSLT,但是由于整个 XML 文档需要保存到内存中,这种方法会和 XmlDocument 方法一样遇到相同的问题。另外,由于开发人员不熟悉 XSLT,了解如何正确使用模板匹配时会遇到较大的困难。

要解决如何处理大型 XML 文档问题的一种方法是使用 XmlReader 读取 XML,读取的同时使用 XmlWriter 将其写出。使用这种方法,整个文档不会同时存入内存中,对 XML 可以进行更精确的更改而不只是追加元素。下面的代码示例读取前面部分的 XML 文档,筛选出所有 ip 元素的值为 "127.0.0.1" 的事件后将其保存为存档文件。

using System;

using System.Xml;

using System.IO;

using System.Text;

public class Test2{

static string ipKey;

static string httpMethodKey;

static string fileKey;

static string dateKey;

static string referrerKey;

public static void WriteAttributes(XmlReader reader, XmlWriter writer){

if(reader.MoveToFirstAttribute()){

do{

writer.WriteAttributeString(reader.Prefix,

reader.LocalName,

reader.NamespaceURI,

reader.Value);

}while(reader.MoveToNextAttribute());

reader.MoveToElement();

}

}

public static void WriteEvent(XmlWriter writer, string ip,

string httpMethod, string file,

string date, string referrer){

writer.WriteStartElement("event");

writer.WriteElementString("ip", ip);

writer.WriteElementString("http_method", httpMethod);

writer.WriteElementString("file", file);

writer.WriteElementString("date", date);

if(referrer != null) writer.WriteElementString("referrer", referrer);

writer.WriteEndElement();

}

public static void ReadEvent(XmlReader reader, out string ip,

out string httpMethod, out string file,

out string date, out string referrer){

ip = httpMethod = file = date = referrer = null;

while( reader.Read() && reader.NodeType != XmlNodeType.EndElement){

if (reader.NodeType == XmlNodeType.Element) {

if(reader.Name == ipKey){

ip = reader.ReadString();

}else if(reader.Name == httpMethodKey){

httpMethod = reader.ReadString();

}else if(reader.Name == fileKey){

file = reader.ReadString();

}else if(reader.Name == dateKey){

date = reader.ReadString();

// reader.Read(); // 使用结尾标记

}else if(reader.Name == referrerKey){

referrer = reader.ReadString();

}

}//if

}//while

}

public static void Main(string[] args){

string ip, httpMethod, file, date, referrer;

//使用用于进行比较的字符串设置 XmlNameTable

XmlNameTable xnt = new NameTable();

ipKey = xnt.Add("ip");

httpMethodKey = xnt.Add("http_method");

fileKey = xnt.Add("file");

dateKey = xnt.Add("date");

referrerKey = xnt.Add("referrer");

//使用上面的 XmlNameTable 加载 XmlTextReader

XmlTextReader xr = new XmlTextReader("logfile.xml", xnt);

xr.WhitespaceHandling = WhitespaceHandling.Significant;

XmlValidatingReader vr = new XmlValidatingReader(xr);

vr.ValidationType = ValidationType.None;

vr.EntityHandling = EntityHandling.ExpandEntities;

StreamWriter sw =

new StreamWriter ("logfile-archive.xml", false, Encoding.UTF8 );

XmlWriter xw = new XmlTextWriter (sw);

vr.MoveToContent(); // 移到文档元素

xw.WriteStartElement(vr.Prefix, vr.LocalName, vr.NamespaceURI);

WriteAttributes(vr, xw);

vr.Read(); // 移到文档元素的第一个 <event> 子元素

// 写出不是 127.0.0.1(本地主机)中的事件

do

{

ReadEvent(vr, out ip, out httpMethod,

out file, out date, out referrer);

if(!ip.Equals("127.0.0.1")){

WriteEvent(xw,ip, httpMethod, file, date, referrer);

}

vr.Read(); //移到下一个 <event> 元素或 <logfile> 的结尾标记

} while(vr.NodeType == XmlNodeType.Element);

Console.WriteLine("Done");

vr.Close();

xw.Close();

}

}

上面的代码示例在写入到 logfile-archive.xml 文件中时会导致下面的输出:

<logfile>

<event>

<ip>192.168.0.1</ip>

<http_method>POST</http_method>

<file>comments.aspx</file>

<date>1999-05-05T19:25:13.238220-08:00</date>

</event>

</logfile>

除了使用 XmlReader 到 XmlWriter 的链之外,上面代码的另一个有趣方面是,使用 ReadEvent() 方法检查元素标记名称时使用 NameTable 提高了文本比较的性能。在 XmlReader 中使用这种方法检查元素的标记名称的优点在如下的 MSDN 文档主题中进行了概述:Object Comparison Using XmlNameTable with XmlReader(英文)。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有