分享
 
 
 

C#中使用XML——编写XML

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

在我的上一篇文章《C#中使用XML——读取XML》中和大家讨论了如何使用.NET Framework中提供的类在C#中读取XML以及读取的一些相关概念,那么今天就说一说如何在C#中编写XML文档,起初我觉得用编程的方式去编写XML简直就是自讨苦吃,后来想想还是觉得挺有用的,我想Microsoft那班家伙能编出这些类来应该不是仅仅为了向比尔i盖茨交差吧!至于它的用处嘛……比如说做安装程序啊!我们可以根据在安装过程中用户所选的选项以及一些设置来生成相应的XML文档再根据XML文档来初始化我们的应用程序。空洞的话不说那么多了,下面我们来了解一下具体的实现细节。

要编写XML同样是采用流的概念,在.NET中编写XML的细节是作为XmlWriter类来实现的,但该类是抽象类不能够实例化,为此,我们要想在程序中访问它的方法以实现编写XML的愿望,必需使用它的派生类XmlTextWriter,该类提供了一系列的属性和方法为我们编写XML做准备,下面将详细的介绍这个类:

构造函数:

public XmlTextWriter(TextWriter);

public XmlTextWriter(Stream, Encoding);

public XmlTextWriter(string, Encoding);

第一个构造函数是把现有的TextWriter实例传递过去,System.IO.TextWriter类是一个有序的字符流

第二个构造函数是把要写入的流作为第一个参数,第二个参数是指定XML文档的编码方式,默认是UTF8,可取Encoding的枚举值,流可以是FileStream,MemoryStream,NetworkStream等等

第三个构造函数是把希望写入的文件名当作一个字符串(如果存在,就重写该文件)传递给第一个参数,第二个参数指定编码方式

常用的方法:

WriterStartDocument()和WriterEndDocument()方法:

第一个方法用来编写XML声明部分,如:<?xml version=”1.0” encoding=”UTF-8” ?>

第二个方法用来关闭任何打开的元素或属性并将编写器重新设置为 Start 状态。

WriterStartElement()和WriteEndElement()方法:

第一个方法用来写出指定的开始标记,该方法有以下几个重载:

WriterStartElement(string localname)

使用传递的字符串作为元素的本地名称

WriterStartElement(string localname,string namespace)

第一个参数指定元素的本地名称,第二个参数指定元素所在的命名空间

WriterStartElement(string prefix,string localname,string namespace)

第一个参数指定元素的前缀,第二个参数指定元素的本地名称,第三个参数指定元素所在的命名空间

第二个方法用来写出与开始元素对应的关闭元素,如果开始元素不包含任何内容,将用一个”/>”做为关闭元素

WriterStartAttribute()和WriterEndAttribute()方法:

第一个方法用于编写一个属性的开头,该方法有两个重载:

WriterStartAttribute(string localname,string namespace)

第一个参数指定属性的本地名称,第二个参数指定属性所在的命名空间

WriterStartAttribute(string prefix,string localname,string namespace)

第一个参数指定属性的前缀,第二个参数指定属性的本地名称,第三个参数指定属性所在的命名空间

第二个方法用于关闭WriterStartAttribute创建的属性

WriterElementString()方法:

该方法可以创建一个包含字符串值的元素,它有以下重载:

WriterElementString(string localname,string value)

如果编写这样的代码:WriterElementString(“para”,”Some text”) 将输出:<para>Some text</para>

WriterElementString(string localname,string namespace,string value)

如果编写这样的代码:WriterElementString(“para”,”http://www.w3.org/ns”,”Some text”) 将输出:<para xmlns=”http://www.w3.org/ns”>Some text</para>

如果编写嵌套几级的元素可使用WriterStartElement()和WriterEndElement()方法,如果编写直接包含内容的元素可以使用该方法

WriterAttributeString()方法:

类似与WriterElementString()方法,在使用上如果属性的值当中不包含实体可直接使用该方法来写出属性,如果属性值包含实体可使用WriterStartAttribute()和WriterEndAttribute()方法,例如要写出这样的XML——<para author=”Do&0241;a&amp;L.Perez”/>,可编写以下代码:

WriterStartElement(“para”);

WriterStartAttribute(“author”,null);

WriterString(“Do”);

WriterCharEntiry(“~n”);

WriterString(“a”);

WriterCharEntiry(“&”);

WriterString(“L.Perez”);

WriterEndAttribute();

WriterEndElement();

该方法有以下重载:

WriterAttributeString(string localname,string value);

WriterAttributeString(string localname,string namespace,string value);

WriterAttributeString(string prefx, string localname,string namespace,string value);

WriterNode(XmlReader reader,bool defattr)方法:

该方法可以从XmlReader读取器中复制节点并把它们写入XmlWriter流中,第一个参数是XmlReader的实例,第二个参数接受一个布尔值,决定是否复制元素中的属性,考虑下面XML片段:

<para>

<sent>

The<b>XmlWriter</b>class writes XML content to a Stream.

</sent>

</para>

以下代码复制其中的片段,reader代表XmlReader的实例writer代表XmlWriter类的实例:

while(reader.Read())

{

if (reader.Name == ”sent” && reader.NodeType == XmlNodeType.Element)

{

writer.WriterNode(reader,true);

}

}

得到以下输出:

<sent>

The<b>XmlWriter</b>class writes XML content to a Stream.

</sent>

WriterComment(string text)方法:用于写出注释

WriterString(string text)方法:用于写出文本

WriterCData(string text)方法:写出CDATA数据块

WriterBase64(byte[] buffer,int index,int count)方法:将指定的二进制字节编码为 Base64 并写出结果文本

Flush():将缓冲区中的所有内容刷新到基础流,并同时刷新基础流

Close():关闭此流和基础流

以上对XmlTextWriter类的一些重要方法做了简单介绍,下面我们就来看一个例程,看看在程序中如何使用这些方法,照样还是先来看下运行效果图:

Example1按纽将向一个文件写出XML声明和一个元素节点以及节点内的文本,Example2按纽将在Example1的基础上添加属性节点,嵌套元素以及文本,WriteNode按纽使用WriterNode()方法在现有读取器中复制该读取器中的所有元素及属性并写到一个新的XML文档中,Example3按纽将写一份完整的XML文档,Example4按纽在Example3按纽的基础上另外生成一份文档并向该文档中追加CDATA部分,Example5按纽将使用WriterBase64()方法对一幅图片进行编码并将编码后的数据写到XML文档中,Example6按纽将使用Example5按纽中生成的XML读取其中数据并对其中编码数据进行解码最后生成一张图片。

以下是功能实现代码:

namespace XMLWriting

{

using System;

using System.IO;

using System.Text;

using System.Xml;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

/// <summary>

/// Form1 的摘要说明。

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

private System.Windows.Forms.TextBox textBox1;

private System.Windows.Forms.Button button1;

private System.Windows.Forms.Button button2;

private System.Windows.Forms.Button button3;

private System.Windows.Forms.Button button4;

private System.Windows.Forms.Button button5;

private System.Windows.Forms.Button button6;

private System.Windows.Forms.Button button7;

/// <summary>

/// 必需的设计器变量。

/// </summary>

private System.ComponentModel.Container components = null;

public Form1()

{

//

// Windows 窗体设计器支持所必需的

//

InitializeComponent();

//

// TODO: 在 InitializeComponent 调用后添加任何构造函数代码

//

}

/// <summary>

/// 清理所有正在使用的资源。

/// </summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows 窗体设计器生成的代码

/// <summary>

/// 设计器支持所需的方法 - 不要使用代码编辑器修改

/// 此方法的内容。

/// </summary>

private void InitializeComponent()

{

this.textBox1 = new System.Windows.Forms.TextBox();

this.button1 = new System.Windows.Forms.Button();

this.button2 = new System.Windows.Forms.Button();

this.button3 = new System.Windows.Forms.Button();

this.button4 = new System.Windows.Forms.Button();

this.button5 = new System.Windows.Forms.Button();

this.button6 = new System.Windows.Forms.Button();

this.button7 = new System.Windows.Forms.Button();

this.SuspendLayout();

//

// textBox1

//

this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)

| System.Windows.Forms.AnchorStyles.Left)

| System.Windows.Forms.AnchorStyles.Right)));

this.textBox1.Location = new System.Drawing.Point(0, 8);

this.textBox1.Multiline = true;

this.textBox1.Name = "textBox1";

this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;

this.textBox1.Size = new System.Drawing.Size(784, 332);

this.textBox1.TabIndex = 0;

this.textBox1.Text = "";

//

// button1

//

this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));

this.button1.Location = new System.Drawing.Point(0, 344);

this.button1.Name = "button1";

this.button1.TabIndex = 1;

this.button1.Text = "Example1";

this.button1.Click += new System.EventHandler(this.button1_Click);

//

// button2

//

this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));

this.button2.Location = new System.Drawing.Point(88, 344);

this.button2.Name = "button2";

this.button2.TabIndex = 2;

this.button2.Text = "Example2";

this.button2.Click += new System.EventHandler(this.button2_Click);

//

// button3

//

this.button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));

this.button3.Location = new System.Drawing.Point(176, 344);

this.button3.Name = "button3";

this.button3.TabIndex = 3;

this.button3.Text = "WriteNode";

this.button3.Click += new System.EventHandler(this.button3_Click);

//

// button4

//

this.button4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));

this.button4.Location = new System.Drawing.Point(264, 344);

this.button4.Name = "button4";

this.button4.TabIndex = 4;

this.button4.Text = "Example3";

this.button4.Click += new System.EventHandler(this.button4_Click);

//

// button5

//

this.button5.Location = new System.Drawing.Point(352, 344);

this.button5.Name = "button5";

this.button5.TabIndex = 5;

this.button5.Text = "Example4";

this.button5.Click += new System.EventHandler(this.button5_Click);

//

// button6

//

this.button6.Location = new System.Drawing.Point(440, 344);

this.button6.Name = "button6";

this.button6.TabIndex = 6;

this.button6.Text = "Example5";

this.button6.Click += new System.EventHandler(this.button6_Click);

//

// button7

//

this.button7.Location = new System.Drawing.Point(528, 344);

this.button7.Name = "button7";

this.button7.TabIndex = 7;

this.button7.Text = "Example6";

this.button7.Click += new System.EventHandler(this.button7_Click);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);

this.ClientSize = new System.Drawing.Size(784, 373);

this.Controls.Add(this.button7);

this.Controls.Add(this.button6);

this.Controls.Add(this.button5);

this.Controls.Add(this.button4);

this.Controls.Add(this.button3);

this.Controls.Add(this.button2);

this.Controls.Add(this.button1);

this.Controls.Add(this.textBox1);

this.Name = "Form1";

this.Text = "XMLWriting";

this.ResumeLayout(false);

}

#endregion

/// <summary>

/// 应用程序的主入口点。

/// </summary>

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

private void button1_Click(object sender, System.EventArgs e)

{

this.textBox1.Text = string.Empty;

const string fileName = "WriteXml.xml";

XmlTextWriter xmlTxtWt = new XmlTextWriter(fileName,Encoding.UTF8);

// 写XML文档声明

xmlTxtWt.WriteStartDocument();

// 写XML起始元素

xmlTxtWt.WriteStartElement("ct","ContactDetails","http://www.deltabis.com/Contact");

// 写文本

xmlTxtWt.WriteString("This is a XML file");

// 写XML结束元素

xmlTxtWt.WriteEndElement();

// 写关闭文档元素

xmlTxtWt.WriteEndDocument();

xmlTxtWt.Flush(); //刷新

xmlTxtWt.Close();

this.textBox1.Text = ReadXml(fileName);

}

/// <summary>

/// 读取经过编写的XML文件的所有内容

/// </summary>

/// <param name="xmlPath">文件路径</param>

/// <returns>表示内容的字符串</returns>

private string ReadXml(string xmlPath)

{

string xmlStr = string.Empty;

XmlTextReader xmlTxtRd = new XmlTextReader(xmlPath);

xmlTxtRd.MoveToContent();

xmlStr = xmlTxtRd.ReadOuterXml();

xmlTxtRd.Close();

return xmlStr;

}

private void button2_Click(object sender, System.EventArgs e)

{

this.textBox1.Text = string.Empty;

const string fileName = "WriteXml1.xml";

XmlTextWriter xmlTxtWt = new XmlTextWriter(fileName,Encoding.UTF8);

// 设置XML的输出格式,这里使用缩进

xmlTxtWt.Formatting = Formatting.Indented;

// 设置缩进的数量,这里是4个空格,IndentChar属性默认是空格

xmlTxtWt.Indentation = 4;

xmlTxtWt.WriteStartDocument();

xmlTxtWt.WriteStartElement("ct","ContactDetails","http://www.deltabis.com/Contact");

xmlTxtWt.WriteAttributeString("Date","20050121 14:00");

xmlTxtWt.WriteElementString("contact","abcd");

xmlTxtWt.WriteElementString("contact","efgh");

xmlTxtWt.WriteElementString("contact","ijkl");

xmlTxtWt.WriteElementString("contact","mnop");

xmlTxtWt.WriteEndElement();

xmlTxtWt.WriteEndDocument();

xmlTxtWt.Flush();

xmlTxtWt.Close();

this.textBox1.Text = ReadXml(fileName);

}

// 从读取器中复制节点及其内容

private void button3_Click(object sender, System.EventArgs e)

{

XmlTextReader xmlTxtRd = new XmlTextReader("唐诗.xml");

XmlTextWriter xmlTxtWt = new XmlTextWriter("WriteXml2.xml",Encoding.UTF8);

xmlTxtWt.Formatting = Formatting.Indented;

xmlTxtWt.Indentation = 4;

xmlTxtWt.WriteStartDocument();

xmlTxtWt.WriteComment("以下是从读取器中拷贝的节点");

try

{

while(xmlTxtRd.Read())

{

if (xmlTxtRd.NodeType == XmlNodeType.Element)

xmlTxtWt.WriteNode(xmlTxtRd,true);

}

}

catch(Exception exp)

{

MessageBox.Show(exp.ToString());

}

finally

{

xmlTxtWt.Flush();

xmlTxtWt.Close();

xmlTxtRd.Close();

}

this.textBox1.Text = ReadXml("WriteXml2.xml");

}

// 编写一份完整的XML

private void button4_Click(object sender, System.EventArgs e)

{

this.textBox1.Text = string.Empty;

string fileName = "WriteXml3.xml";

XmlTextWriter xmlTxtWt = new XmlTextWriter(fileName,Encoding.UTF8);

xmlTxtWt.Formatting = Formatting.Indented;

xmlTxtWt.Indentation = 4;

xmlTxtWt.WriteStartDocument();

xmlTxtWt.WriteStartElement("ct","ContactDetails","http://www.deltabis.com/Contact");

xmlTxtWt.WriteAttributeString("Date","20050121 16:00");

xmlTxtWt.WriteComment("This document contains contact information.");

xmlTxtWt.WriteStartElement("contact");

xmlTxtWt.WriteAttributeString("title",string.Empty);

xmlTxtWt.WriteStartElement("name");

xmlTxtWt.WriteElementString("firstname","Steven");

xmlTxtWt.WriteElementString("middle",string.Empty);

xmlTxtWt.WriteElementString("lastname","LivingStone-Perez");

xmlTxtWt.WriteFullEndElement();

xmlTxtWt.WriteFullEndElement();

xmlTxtWt.WriteFullEndElement();

xmlTxtWt.WriteEndDocument();

xmlTxtWt.Flush();

xmlTxtWt.Close();

this.textBox1.Text = ReadXml(fileName);

}

// 添加CDATA数据块

private void button5_Click(object sender, System.EventArgs e)

{

this.textBox1.Text = string.Empty;

string fileName = "WriteXml4.xml";

XmlTextWriter xmlTxtWt = new XmlTextWriter(fileName,Encoding.UTF8);

xmlTxtWt.Formatting = Formatting.Indented;

xmlTxtWt.Indentation = 4;

xmlTxtWt.WriteStartDocument();

xmlTxtWt.WriteStartElement("ct","ContactDetails","http://www.deltabis.com/Contact");

xmlTxtWt.WriteAttributeString("Date","20050121 16:00");

xmlTxtWt.WriteComment("This document contains contact information.");

xmlTxtWt.WriteStartElement("contact");

xmlTxtWt.WriteAttributeString("title",string.Empty);

xmlTxtWt.WriteStartElement("name");

xmlTxtWt.WriteElementString("firstname","Steven");

xmlTxtWt.WriteElementString("middle",string.Empty);

xmlTxtWt.WriteElementString("lastname","LivingStone-Perez");

xmlTxtWt.WriteFullEndElement();

xmlTxtWt.WriteStartElement("notes","http://www.deltabis.com/Contact"); // 该节点的命名空间与上面一样,该节点将使用上面的前缀

xmlTxtWt.WriteCData("<securityAlogrithm>88hshshhhdd8*^&@^*^#*&!%~~~(ghj*(**&%^){}^(*&7*(9$%###$@!");

xmlTxtWt.WriteEndElement();

xmlTxtWt.WriteFullEndElement();

xmlTxtWt.WriteFullEndElement();

xmlTxtWt.WriteEndDocument();

xmlTxtWt.Flush();

xmlTxtWt.Close();

this.textBox1.Text = ReadXml(fileName);

}

// 对图片进行编码,并写出

private void button6_Click(object sender, System.EventArgs e)

{

int readByte = 0;

int bytesToRead = 100;

string fileName = "WriteXml5.xml";

this.textBox1.Text = string.Empty;

// 打开图片文件,利用该图片构造一个文件流

FileStream fs = new FileStream(@"D:\03.jpg",FileMode.Open);

// 使用文件流构造一个二进制读取器将基元数据读作二进制值

BinaryReader br = new BinaryReader(fs);

XmlTextWriter xmlTxtWt = new XmlTextWriter(fileName,Encoding.UTF8);

xmlTxtWt.Formatting = Formatting.Indented;

xmlTxtWt.Indentation = 4;

xmlTxtWt.WriteStartDocument();

xmlTxtWt.WriteStartElement("ct","ContactDetails","http://www.deltabis.com/Contact");

xmlTxtWt.WriteStartElement("image");

xmlTxtWt.WriteAttributeString("imageName","03.jpg");

byte[] base64buffer = new byte[bytesToRead];

do

{

readByte = br.Read(base64buffer,0,bytesToRead); //将数据读入字节数组

xmlTxtWt.WriteBase64(base64buffer,0,readByte); //将数组中二进制值编码为Base64并写出到XML文件

}while(bytesToRead <= readByte);

xmlTxtWt.WriteEndElement();

xmlTxtWt.WriteEndElement();

xmlTxtWt.WriteEndDocument();

xmlTxtWt.Flush();

xmlTxtWt.Close();

this.textBox1.Text = ReadXml(fileName);

}

// 解码并生成图片

private void button7_Click(object sender, System.EventArgs e)

{

int readByte = 0;

int bytesToRead = 100;

XmlTextReader xmlTxtRd = new XmlTextReader("WriteXml5.xml");

FileStream fs = new FileStream("newimage.jpg",FileMode.Create);

BinaryWriter bw = new BinaryWriter(fs);

byte[] base64buffer = new byte[bytesToRead];

while(xmlTxtRd.Read())

{

if (xmlTxtRd.NodeType == XmlNodeType.Element && xmlTxtRd.Name == "image")

{

do

{

readByte = xmlTxtRd.ReadBase64(base64buffer,0,bytesToRead);

bw.Write(base64buffer,0,readByte);

}while(readByte <= bytesToRead);

}

}

bw.Flush();

bw.Close();

fs.Close();

xmlTxtRd.Close();

}

}

}

以下是在WriteNode按纽中要使用到的XML文件:

唐诗.xml

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

<唐诗>

<五言绝句>

<作者 字号="太白">李白</作者>

<标题>静夜思</标题>

<内容>床前明月光,疑是地上霜。举头望明月,低头思故乡。</内容>

</五言绝句>

<五言绝句>

<作者 字号="太白">李太白</作者>

<标题>春晓</标题>

<内容>春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。</内容>

</五言绝句>

<五言绝句>

<作者 字号="季凌">王之涣</作者>

<标题>登鹤雀楼</标题>

<内容>白日依山尽,黄河入海流。欲穷千里目,更上一层楼</内容>

</五言绝句>

<五言绝句>

<作者>李清照</作者>

<标题>如梦令</标题>

<内容>昨夜风疏雨骤,浓睡不消残酒,试问卷帘人,却道海棠依旧,知否,知否,应是绿肥红瘦。</内容>

</五言绝句>

</唐诗>

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有