`

C#读写XML文件工具类——满足一切需求

 
阅读更多

C#读写XML文件工具类——满足一切需求

         

       

       还是由于工作的需要,要进行解析XML配置表,虽然感觉XML很简单,因为之前对XML完全空白,所以只能从头学起,那就只能从google开始,找到一些参考①②③见文末,但是总感觉比较零散、混乱,功能不全,只能是例子不能成为工具,所以一直想找一个基本可以满足所有需求的实现。大概熟悉了XML的结构(还是花了好久,虽然比较简单,但是要很C#api中的Node,Element,Attribute等概念对应分得清要一点时间去消化)和C# XmlDocument的api,感觉通过api去实现一些功能还是比较困难或者比较直观,比如我要查找含有属性“lang”的所有结点,这个大概想了下要得遍历,所以迟迟未动手,知道我找到参考④。看了④的文章,很是惊奇,惊呼——原来还有这个东西(Xpath),顿时觉得希望实现自己想要的功能。然后就去查阅W3C School的Xpath的规范。

       有了想法之后,总是忍不住的想动手,刚开始的想法是定义一个StringBuilder xpath变量,然后往后面添加相关操作的Xpath字符串,但是还是感觉情况太多了,函数命名都成问题了(我记得一直命名到AddNode9()),总感觉做的是字符匹配的过程,但是有没有“正则表达式”的智能,然后我有想起一直的困惑——对于情况很多再加上不同情况的组合的问题解决起来很被动,有种被耍的感觉。那只有彻底解决,其实这就是“正则表达式”的原理——有限状态机,好在我之前看过“有限状态机”的理论。

       我早上三点起来(哎,一直挺矛盾的),我在CodeProject上找FSM的代码(还真不少),果断下载了几个,对代码仔细研读一番,最后感觉这个问题用FSM实在是大材小用,发现之前感觉的“不智能”都不会了,可以通过约束使用规范避免,所以还是回归到Xpath上,然后就有了下面的代码(测试了几个都没有问题),后面部分(有注释的直接copy③),实在是没有时间(一会还要去上班),然后就是功能还不完善,以后有时间一并补上(如果你有补充希望能够分享下,谢谢)。

下面的代码(假定你具有Xpath的基础)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
/// Code By D.S.Qiu
namespace XmlParse
{
    class XmlUtils
    {
        public static StringBuilder xpath = new StringBuilder();
        public static XmlDocument xmlDoc=new XmlDocument();
        public static XmlElement root = null;
        public static void AddCommint(string commit)
        {
            xpath.Append(commit);
        }
        public static void AddNode(string name)
        {
            xpath.Append("/" + name);
        }
        public static void AddNodeRegardlessPosition(string name)
        {
            xpath.Append("//" + name);
        }
        public static void AddNodeByIndex(string name, string index)
        {
            xpath.Append("/" + name + "[" + index + "]");
        }
        public static void AddNodeByIndexRegardlessPosition(string name, string index)
        {
            xpath.Append("//" + name + "[" + index + "]");
        }
        public static void AddNodeByValue(string name, string value, string operatorstr = "=")
        {
            xpath.Append("[" + name + operatorstr + value + "]");
        }
        public static void AddIndex(string index)
        {
            xpath.Append("[" + index + "]");
        }
        public static void AddAttribute(string name)
        {
            xpath.Append("[@" + name + "]");
        }
        public static void AddAttributeByValue(string name,  string value,string operatorstr="=")
        {
            xpath.Append("[@" + name +operatorstr+value+ "]");
        }
        public static void AddAnyNode()
        {
            xpath.Append("/*" );
        }
        public static void AddAnyNodeRegardlessPosition()
        {
            xpath.Append("//*");
        }
        public static void AddAnyAttribute()
        {
            xpath.Append("[@*]");
        }
        public static void Load(string filename)
        {
            xmlDoc.Load(filename);
            root = xmlDoc.DocumentElement;
        }

        public static XmlNodeList Commit(string commit=null)
        {
            XmlNodeList xn=null;
            if (commit != null)
            {
                  xn= root.SelectNodes(commit);
            }

            else if (xpath.Length != 0)
            {
                xn= root.SelectNodes(XmlUtils.xpath.ToString());
                xpath.Clear();
            }
            
            return xn;
        }
        public void addElement(string path, string node_root, string node_name, string node_text, string att_name, string att_value)
        {
            Load(path);
 
            XmlNodeList nodeList = xmlDoc.SelectSingleNode(node_root).ChildNodes;//获取bookstore节点的所有子节点
            //判断是否有节点,有节点就遍历所有子节点,看看有没有重复节点,没节点就添加一个新节点
            if (nodeList.Count > 0)
            {
                foreach (XmlNode xn in nodeList)//遍历所有子节点 
                {
                    XmlElement xe = (XmlElement)xn;//将子节点类型转换为XmlElement类型 
                    if (xe.GetAttribute(att_name) != att_value)
                    {
                        XmlNode xmldocSelect = xmlDoc.SelectSingleNode(node_root);   //选中根节点
                        XmlElement son_node = xmlDoc.CreateElement(node_name);    //添加子节点 
                        son_node.SetAttribute(att_name, att_value);     //设置属性
                        son_node.InnerText = node_text;    //添加节点文本
                        xmldocSelect.AppendChild(son_node);       //添加子节点
                        xmlDoc.Save(path);          //保存xml文件
                        break;
                    }
                }
 
            }
            else
            {
                XmlNode xmldocSelect = xmlDoc.SelectSingleNode(node_root);   //选中根节点
                XmlElement son_node = xmlDoc.CreateElement(node_name);    //添加子节点 
                son_node.SetAttribute(att_name, att_value);     //设置属性
                son_node.InnerText = node_text;    //添加节点文本
                xmldocSelect.AppendChild(son_node);       //添加子节点
                xmlDoc.Save(path);          //保存xml文件
            }
        }
 
        /// <summary>
        /// 修改节点的内容
        /// </summary>
        /// <param name="path">xml文件的物理路径</param>
        /// <param name="node_root">根节点名称</param>
        /// <param name="new_text">节点的新内容</param>
        /// <param name="att_name">节点的属性名</param>
        /// <param name="att_value">节点的属性值</param>
        public void UpdateElement(string path, string node_root, string new_text, string att_name, string att_value)
        {
            Load(path);
            XmlNodeList nodeList = xmlDoc.SelectSingleNode(node_root).ChildNodes;//获取bookstore节点的所有子节点 
            foreach (XmlNode xn in nodeList)//遍历所有子节点 
            {
                XmlElement xe = (XmlElement)xn;//将子节点类型转换为XmlElement类型 
                if (xe.GetAttribute(att_name) == att_value)
                {
                    xe.InnerText = new_text;    //内容赋值
                    xmlDoc.Save(path);//保存 
                    break;
                }
            }
 
        }
 
        /// <summary>
        /// 删除节点
        /// </summary>
        /// <param name="path">xml文件的物理路径</param>
        /// <param name="node_root">根节点名称</param>
        /// <param name="att_name">节点的属性名</param>
        /// <param name="att_value">节点的属性值</param>
        public void deleteNode(string path, string node_root, string att_name, string att_value)
        {
            Load(path);
            XmlNodeList nodeList = xmlDoc.SelectSingleNode(node_root).ChildNodes;
            XmlNode root = xmlDoc.SelectSingleNode(node_root);
            foreach (XmlNode xn in nodeList)
            {
                XmlElement xe = (XmlElement)xn;
                if (xe.GetAttribute(att_name) == att_value)
                {
                    //xe.RemoveAttribute("name");//删除name属性 
                    xe.RemoveAll();//删除该节点的全部内容 
                    root.RemoveChild(xe);
                    xmlDoc.Save(path);//保存 
                    break;
                }
 
            }
        }
    }
}
        

        最后说下自己的感悟:因为刚毕业,工作中的很多问题都没有接触过,虽然都很简单,但是要做出来还是要花一定时间的,所以D.S.Qiu觉得学一个新东西一定要彻底弄懂,以后才能事半功倍,还要记录一些学习过程中遇到的问题和解决问题的一些方法和心得。

       

增补于2015.12.08

 

        之前因为后面找到XmlParser(查看博客),因为一直都只是Read(解析)没有Write,然后最近发布Window Phone 版本需要修改Unity 导出的 vs 工程的 .csproj 文件,其实就是一个xml文件,一开始都是google,最后还是发现自己很早写这篇XPath的博客,然后就打算整理下一个工具类。本来打算自己实现一个 XPath 类以实现链式调用(chain call) ,结果折腾到今天凌晨5点,才发现其实完全没有必要或者做不到想象中的各种爽,最后把函数整理了下,不得不睡觉……

        没睡几个小时,一大早来公司,测试,发现各种返回为 null ,明明和别人的 XPath 是一样,就是得不到结果。

        一直在google 最后发现还是有命名空间的问题,然后最后用 XPathNavigator.Evaluate() 返回 XPathNodeIterator 的 Count 一直为0,发现要调用 MoveNext 才能获得值。

        花了一天才解决上面两个问题,最后才发现MSDN上面都有说道,所以一定要细致,不放过任何细节,不放过任何细节,不放过任何细节……

 

整理下附上最新的代码:

 

using System.Text;
using System.Xml;
/// Code By D.S.Qiu
/*
 * 具体规则可参考:http://www.w3school.com.cn/xpath/index.asp
 * 或者:https://msdn.microsoft.com/en-us/library/ms256471(v=vs.110).aspx
 * https://msdn.microsoft.com/en-us/library/ms256453(v=vs.110).aspx
 *
 * 1.节点
 * a)./author 或 author  当前节点的所有<author>子节点
 * b)/author             根节点的所有<author>子节点
 * c)//author            根节点的所有<author>节点   注 .//author 当前节点所有<author>节点
 * d).                   选取当前节点
 * e)..                  选取当前节点的父节点
 * f)@                   选取属性

author/first-name  All <first-name> elements within an <author> element of the current context node.
bookstore//title  All <title> elements one or more levels deep in the <bookstore> element (arbitrary descendants).
.//title  All <title> elements one or more levels deep in the current context. Note that this situation is essentially the only one in which the period notation is required.

@style  The style attribute of the current element context.
price/@exchange The exchange attribute of <price> elements within the current context.
book/@style The style attribute of all <book> elements.
Note that the following example is not valid, because an attribute cannot have any children.
price/@exchange/total
 *
 * 2.谓词
 * a)[]                  谓语被嵌在方括号中
 *     /bookstore/book[price>35.00]	选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
 *     /bookstore/book[position()<3]	选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
 *     //title[@lang='eng']	选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
 *     book[excerpt]  All <book> elements that contain at least one <excerpt> element.
       book[excerpt]/title  All <title> elements inside <book> elements that contain at least one <excerpt> element.
       book[excerpt]/author[degree]  All <author> elements that contain at least one <degree> element, and are inside of <book> elements that contain at least one <excerpt> element.
       book[author/degree]  All <book> elements that contain at least one <author> element with at least one <degree> child element.
       book[excerpt][title] All <book> elements that contain at least one <excerpt> element and at least one <title> element.

 * b)*                   匹配任何元素节点
 * c)@*                  匹配任何属性节点
 * d)node()              匹配任何类型的节点
 *
 * 3.轴
 * a)ancestor	选取当前节点的所有先辈(父、祖父等)。
 * b)ancestor-or-self	选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
 * c)attribute	选取当前节点的所有属性。
 * d)child	选取当前节点的所有子元素。
 * e)descendant	选取当前节点的所有后代元素(子、孙等)。
 * f)descendant-or-self	选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
 * g)following	选取文档中当前节点的结束标签之后的所有节点。
 * h)namespace	选取当前节点的所有命名空间节点。
 * i)parent	选取当前节点的父节点。
 * j)preceding	选取文档中当前节点的开始标签之前的所有节点。
 * k)preceding-sibling	选取当前节点之前的所有同级节点。
 * l)self	选取当前节点。
 * 步的语法:
     轴(axis)
     定义所选节点与当前节点之间的树关系
     节点测试(node-test)
     识别某个轴内部的节点
     零个或者更多谓语(predicate)
     更深入地提炼所选的节点集
     轴名称::节点测试[谓语]
    实例
    child::book	选取所有属于当前节点的子元素的 book 节点。
    attribute::lang	选取当前节点的 lang 属性。
    child::*	选取当前节点的所有子元素。   和 * 一致
    child::text()	选取当前节点的所有文本子节点。 和 . 一致
    child::node()	选取当前节点的所有子节点。   和 .. 一致
 *
 *
Precedence
Precedence order (from highest precedence to lowest) between Boolean and comparison operators is shown in the following table.

Precedence	Operators	Description
1 ( ) Grouping
2 [ ] Filters
3 /   Path operations
  //
4 &lt;  Comparisons
  &lt;=
  &gt;
  &gt;=
5 = Comparisons
  !=
6 | Union
7 not() Boolean not
8 and Boolean and
9 or  Boolean or
 *
 */
using UnityEngine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml.XPath;


public static class XmlExtension
{
	

	public static XmlNode UpdateAttribute(this XmlNode node,string name,string value)
	{
		XmlAttribute newAttr = node.OwnerDocument.CreateAttribute(name);
		newAttr.Value = value;
		//xn.Attributes.RemoveNamedItem(attributeValues[i]);
		node.Attributes.SetNamedItem(newAttr);     //设置属性
	    return node;
	}

    public static XmlNode DeleteAttribute(this XmlNode node, string name)
    {
        if (node.Attributes == null)
        {
            Debug.Log("This XMLNode have no attribute!");
        }
        else
        {
            XmlAttribute attribute = node.Attributes[name];
            node.Attributes.Remove(attribute);
        }
        return node;
    }

	public static XmlNode DeleteElement(this XmlNode node,string xpath)
	{
		XmlNodeList rootNodes = node.SelectNodes(xpath);
		if(rootNodes == null || rootNodes.Count == 0)
		{
			Debug.Log(string.Format("Not Found the node {0} in the XmlNode!",xpath));
			return node;
		}
		//遍历所有子节点
		foreach (XmlNode xn in rootNodes)
		{
			node.RemoveChild(xn);
		}
	    return node;
	}

    /// <summary>
    /// 
    /// </summary>
    /// <param name="node"></param>
    /// <param name="xpath"></param>
    /// <param name="nodeName"></param>
    /// <param name="nodeText"></param>
    /// <returns>方便chain call</returns>
	public static System.Xml.XmlElement AppendElement(this XmlNode node,string xpath,string nodeName,string nodeText)
	{
		XmlNode rootNode = node.SelectSingleNode(xpath);
		if(rootNode == null )
		{
			Debug.Log(string.Format("Not Found the node {0} in the XmlNode!",xpath));
			return null;
		}
       
        XmlElement sonNode = node.OwnerDocument.CreateElement(nodeName);    //添加子节点
	    sonNode.InnerText = nodeText;
        rootNode.AppendChild(sonNode);       //添加子节点
        return sonNode;
	}

    public static System.Xml.XmlElement AppendElement(this XmlNode node,  string nodeName, string nodeText = "")
    {
        XmlElement sonNode = node.OwnerDocument.CreateElement(nodeName);    //添加子节点
        sonNode.InnerText = nodeText;
        node.AppendChild(sonNode);       //添加子节点
        return sonNode;
    }
}

/// <summary>
/// 默认把xml的命名工具都去掉,方便使用 XPath 表达式,保存之后再替换回来
/// 所有解析必须在Begin 和 End 之间调用
/// </summary>
public class XmlUtility
{


    /*public class XPath
    {
        public const string FilterFormat = "[{0}]";
		public const string StepFormat = "{0}::{1}[{2}]";


        private static StringBuilder s_content = new StringBuilder();

        public XPath Clear()
        {
            s_content = new StringBuilder();
            return this;
        }

		public XPath Append(string path)
		{
			s_content.Append (path);
			return this;
		}

		public XPath AppendFormat(string format,params string[] param)
		{
			return Append(string.Format(format, param));
		}

        public XPath AppendNode(string nodeName)
        {
            return Append("/" + nodeName);
        }

		public XPath AppendAnyNode(string nodeName)
		{
			return Append("//" + nodeName);
		}

        public XPath AppendFilter(string filter)
        {
            return AppendFormat(FilterFormat, filter);
        }

		public XPath AppendStep(string axis,string nodeTest ="*",string predicate = "*")
		{
			return AppendFormat (StepFormat, axis, nodeTest, predicate);
		}

    }*/

    public const string Xmlns = "xmlns";
    public const string XmlnsReplace = "xmlns_replace";

    private static XmlDocument xmlDoc = null;//new XmlDocument();
    private static bool replaceXmlns = false;
    private static string xmlPath;


    public static void Begin(string xmlPath, bool replaceXmlns = true)
    {
        XmlUtility.xmlPath = xmlPath;
        XmlUtility.replaceXmlns = replaceXmlns;
        Load(xmlPath, replaceXmlns);
    }

    public static void End(bool save = true)
    {
        if(save)
            Save(XmlUtility.xmlPath,XmlUtility.replaceXmlns);
        xmlDoc = null;
    }

	private static void Load(string xmlPath,bool replaceXmlns)
	{
		xmlDoc = new XmlDocument ();
		try
		{
		    string xmlText = File.ReadAllText(xmlPath);
            //去掉命名空间
		    if (replaceXmlns)
                xmlText = xmlText.Replace(Xmlns, XmlnsReplace);
			xmlDoc.LoadXml(xmlText);
		}
		catch(Exception ex)
		{
			Debug.LogError("XmlDocument Load Error: " + ex.ToString());
		}
	}

    private static void Save(string xmlPath, bool replaceXmlns)
    {
        if (xmlDoc == null)
            return ;
        try
        {
            xmlDoc.Save(xmlPath);
            //恢复默命名空间
            if (replaceXmlns)
            {
                string xmlText = File.ReadAllText(xmlPath);
                xmlText = xmlText.Replace(XmlnsReplace,Xmlns);
                File.WriteAllText(xmlPath,xmlText);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError("XmlDocument Save Error: " + ex.ToString());
        }
    }


    /// <summary>
    /// The result of the expression (Boolean, number, string, or node set). This maps to Boolean, Double, String, or
    /// XPathNodeIterator objects respectively.
    /// 注意:如果返回的是 XPathNodeIterator 不会获得所有元素,必须帝调用MoveNext()才能取得元素
    /// 查看:https://msdn.microsoft.com/en-us/library/system.xml.xpath.xpathnodeiterator.aspx 的 Remarks
    /// </summary>
    /// <param name="xmlPath">Xml path.</param>
    /// <param name="xpath">Xpath.</param>
    public static object Evaluate(string xpath)
    {
        //XmlDocument xmlDoc = Load (xmlPath);
        if (xmlDoc == null)
            return null;
        XPathNavigator navigator = xmlDoc.CreateNavigator();
        try
        {
            XPathExpression expression = navigator.Compile(xpath);
            return navigator.Evaluate(expression);
        }
        catch (Exception ex)
        {
            Debug.LogError("XPath Evalute Error: " + ex.ToString());
            return null;
        }
    }


	public static XmlNode FindElement(string xpath)
	{
		//XmlDocument xmlDoc = Load (xmlPath);
		if (xmlDoc == null)
			return null;
		var test = xmlDoc.SelectNodes (xpath);
		return xmlDoc.SelectSingleNode (xpath);
	}

	public static XmlNodeList FindElements(string xpath)
	{
		//XmlDocument xmlDoc = Load (xmlPath);
		if (xmlDoc == null)
			return null;
		return xmlDoc.SelectNodes (xpath);
	}


	public static void AppendElement(string xpath,string nodeName,string nodeText)
	{
		//XmlDocument xmlDoc = Load (xmlPath);
		if (xmlDoc == null)
			return;
		xmlDoc.AppendElement (xpath,nodeName,nodeText);
		//xmlDoc.Save (xmlPath);
	}

	public static void DeleteElement(string xpath)
	{
		//XmlDocument xmlDoc = Load (xmlPath);
		if (xmlDoc == null)
			return;
		xmlDoc.DeleteElement (xpath);
		//xmlDoc.Save (xmlPath);
	}  

}

 

        如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。

        转载请在文首注明出处:http://dsqiu.iteye.com/blog/1888476

更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风)

 

 

参考:

jackyong: http://www.cnblogs.com/liuyong/archive/2011/01/26/Jackyong.html

Eric Sun: http://www.cnblogs.com/mingmingruyuedlut/archive/2011/01/26/1945107.html

永恒浪子: http://www.cnblogs.com/hulang/archive/2010/12/30/1922298.html

顶天的蚂蚁: http://www.cnblogs.com/RiseSoft/archive/2012/03/17/2404007.html

⑤W3C School: http://www.w3school.com.cn/xpath/xpath_syntax.asp

 
1
7
分享到:
评论

相关推荐

    基于C#实现XML文件读取工具类

    主要介绍了基于C#实现XML文件读取工具类,涉及C#针对XML文件各节点获取的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

    C#全能速查宝典

    《C#全能速查宝典》共分为8章,分别介绍了C#语言基础、Windows窗体及常用控件、Windows高级控件、控件公共属性、方法及事件、数据库开发、文件、数据流与注册表、GDI+绘图技术和C#高级编程,共包含562个C#编程中常用...

    xml工具脚本

    Unity下基于C#的XML工具类,可实现XML的增删改查,Android移动端读写

    超级多的C#辅助类大全

    40. 共用工具类 41. 其他 42. 分词辅助类 43. 分页 44. 加密解密 45. 压缩解压缩 46. 各种验证帮助类 47. 图片 48. 图片操作类 49. 图片验证码 50. 处理多媒体的公共类 51. 处理枚举类 52. 字符串 53. ...

    Unity3D_XMLToEgg:基于C#编写的Unity3D工具,利用反射和泛型通过读取XML文件创建新的实例并且为实例赋值。原型,具体内容需要具体配置

    Unity3D_XMLToEgg 基于C#编写的Unity3D工具,利用反射和泛型通过读取XML文件创建新的实例并且为实例赋值。 但是我更喜欢Unity3D_XML2O这个名字,O意味着object也象征着鸡蛋。 XMLToEgg or called XML2O can convert ...

    C#常用公共类源码(100多个)可以直接使用.rar

    ,RDLC直接打印帮助类,ResourceManager,RMB,SqlHelper,SQL语句拦截器,URL的操作类,VerifyCode,XML操作类,上传下载,二维码操作类,共用工具类,其他,分词辅助类,分页 ,加密解密,压缩解压缩,各种验证帮助类,图片,图片操作...

    C#实用类集DdotNet

    200多个工具类 .cs 文件读写 文件夹读写 各种加密解密 XML操作 #region 获得用户IP /// /// 获得用户IP /// public static string GetUserIp() { string ip; string[] temp; bool isErr = false; if ...

    C#开发实例大全(基础卷).软件开发技术联盟(带详细书签) PDF 下载

    实例107 使用哈希表对XML文件进行查询 127 实例108 计算两个矩形矩阵的乘积 129 第5章 面向对象编程技术 131 5.1 属性和方法 132 实例109 使用属性存储用户编号和姓名 132 实例110 通过定义方法求一个数的平方 133 ...

    C#完整教程

    6.1 用流读写文件 137 6.2 File类和FileInfo类 138 6.3 Directory类和DirectoryInfo类 140 6.4 例子:查找文件 143 6.5 例子:拆分和合并文件 144 习题: 145 第七章 多线程程序设计 146 7.1 线程类(Thread)的属性和...

    C#源码大集合 02(共3卷)

    │ │ ├─实例118 如何读写XML文件 │ │ ├─实例120 如何取得聚合函数返回值 │ │ ├─实例121 如何直接操作主从关系表 │ │ ├─实例122 如何以Web方式查询Access数据库 │ │ ├─实例123 如何使用列表视图...

    C# 3.0完全自学宝典 (F)

    AccessXML 演示如何读写XML文件数据 第17章(\ch17) 示例描述:介绍.NET进行TCP/IP网络通讯应用程序开发。 SocketListen 演示Socket进行服务端开发实例 SimpleIMServer SimpleIM软件服务器端实例 第18章(\ch...

    C#源码大集合 03(共3卷)

    │ ├─实例115 如何创建SQL Server数据库 │ │ ├─实例116 如何读写SQL Server数据库 │ │ ├─实例117 如何更新SQL Server数据库 │ │ ├─实例118 如何读写XML文件 │ │ ├─实例120 如何取得...

    C#源码大集合 01(共3卷)

    │ ├─实例115 如何创建SQL Server数据库 │ │ ├─实例116 如何读写SQL Server数据库 │ │ ├─实例117 如何更新SQL Server数据库 │ │ ├─实例118 如何读写XML文件 │ │ ├─实例120 如何取得...

Global site tag (gtag.js) - Google Analytics