入职公司接到的第一个项目,开发一个C#小程序给同事用。之前有实习生写过一版,被同事们吐槽慢不准且易崩。所以我的目标是,参考之前的代码,保证逻辑清晰和准确率的同时提升速度。

 程序功能:比较两个版本xml配置文件内容差异。比较的是所有叶节点属性值的区别。属性值可多选。

程序输入:用户指定两个待比较文件。

程序输出:一个excel表格。用颜色标识“新增项”,“删除项”,“更改项”等。


xml文件长这样: 两个文件有各有增添删改的情况。

xml差异比对java xml文件数据比对_C# 读取Xml到dataTable

最终得到的Excel长这样:

xml差异比对java xml文件数据比对_C#  比较dataTable_02


目录

组成文件

1. Main_frm.cs:窗体文件。主界面,用户指定两个待比较的xml文件,选择待比较的属性。

2.Export.cs :窗体文件。导出界面,用于指定保存比较结果的Excel路径,显示导出进度,以及结束按钮。

3. readXmlToDt.cs:类文件。用于将xml文件读到一个dataTable中。

4.compareDt.cs:类文件。在两个dataTable所有列名相同的情况下,比较相同ID行的属性值是否相同。

5. exportDtToExcel.cs:类文件。将dataTable输出到Excel文件中。

6.colorExcel.cs:类文件。用C#在Excel文件中创建宏(VB),应用此宏来标识特定条件的文本。



组成文件

1. Main_frm.cs:窗体文件。主界面,用户指定两个待比较的xml文件,选择待比较的属性。

xml差异比对java xml文件数据比对_C# 读取Xml到dataTable_03

主界面读取三个值,分别为路径A、路径B、被选属性(以字符串的形式读入)

public static string pathA = "";//用来存路径A
public static string pathB = "";//用来存路径B
public static string attrSelected = "";//用来存放被选择的EC属性

选择文件A:   应用知识点  打开文件对话框OpenFileDialog      

MessageBox.Show 方法

private void btnSelectFileA_Click(object sender, EventArgs e)
        {//选择待比较文件A
            OpenFileDialog fileAPath = new OpenFileDialog();
            fileAPath.Title = "选择文件A";
            fileAPath.Filter = "xml文件(*.xml)|*.xml";//过滤文件类型只能为xml
            fileAPath.RestoreDirectory = false;//每次打开位置与上一次相同
            if (fileAPath.ShowDialog() == DialogResult.OK)
            {
                txtFileAPath.Text = System.IO.Path.GetFullPath(fileAPath.FileName);
                pathA = txtFileAPath.Text;//将路径A保存
                if (pathA == pathB)
                {//如果与另一路径重复,将清空
                    MessageBox.Show("与FileB重复!请重新选择", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    pathA = "";
                    txtFileAPath.Text = "";
                }
            }
        }

选择文件B,同上。

点击继续按钮,将被选择的属性值和路径AB保存。此处将属性存入一个字符串,后面再分割字符串。

private void btnGoon_Click(object sender, EventArgs e)
        {  //选择属性Attr
            attrSelected = (chkattr1.Checked ? "attr1 " : "") +
                (chkattr2.Checked ? "attr2 " : "") +
                (chkattr3.Checked ? "attr3 " : "") +
                (chkattr4.Checked ? "attr4 " : "") +
                (chkattr5.Checked ? "attr5 " : "") +
                (chkattr6.Checked ? "attr6 " : "") +
                (chkattr7.Checked ? "attr7 " : "") +
                (chkattr8.Checked ? "attr8 " : "");

             //点击Goon开启另一个窗口
             //点击Goon将两个绝对路径pathA和pathB保存
             //点击Goon将属性们attrSelected保存
            if (attrSelected == "")
            {//如果属性未选择
                MessageBox.Show("请选择属性!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {//如果有路径未选择
                if (pathA == "" || pathB == "")
                {
                    MessageBox.Show("请确认文件选择是否正确!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    Export result_frm = new Export();//开启Export界面
                    result_frm.StartPosition = FormStartPosition.CenterScreen;//开始在屏幕中央
                    result_frm.Show();
                }
            }
        }

2.Export.cs :窗体文件。导出界面,用于指定保存比较结果的Excel路径,显示导出进度,以及结束按钮。

xml差异比对java xml文件数据比对_C#比较xml配置文件_04

点击按钮选择比较结果的保存路径:

string strsavePath = "";
private void btnSelectSavePath_Click(object sender, EventArgs e)
        {//选择保存路径,并显示在textbox中
            SaveFileDialog savePath = new SaveFileDialog();
            savePath.Title = "保存文件";
            savePath.Filter = "xls文件(*.xls)|*.xls";
            savePath.RestoreDirectory = false;

            if (savePath.ShowDialog() == DialogResult.OK)
            {
                strsavePath = savePath.FileName;
            }
            else
            {
                strsavePath = ""; 
            }
            txtSavePath.Text = strsavePath;       
        }

如果文件路径被用户手动更改,需要重新读入:

private void txtSavePath_TextChanged(object sender, EventArgs e)
        {
            strsavePath = txtSavePath.Text;
            prgSave.Value = 0;
            lblExport.Visible = false;
            picsSuccess.Visible = false;
        }

点击导出按钮,开始读入,比较,保存,显示。

步骤1:检测文件是否被打开。如果保存路径的文件已经存在并被打开,则不能写入结果,因此要检测此文件是否被占用。

思路是,使用文件流查看此文件是否可以被打开,若可以被打开说明没有被占用。

using System.IO;
//检查Excel文件是否已被打开
            if (File.Exists(strsavePath))
            {
                bool op = IsFileInUse(strsavePath);
                if (op)
                {
                    MessageBox.Show("您要保存的文件被占用!\n请关闭文件或重新选择路径");
                    return;
                }
            }
public static bool IsFileInUse(string fileName)
        {//检测文件是否被占用
            bool inUse = true;
                      FileStream fs = null;
            try
            {
                fs = new FileStream(fileName, FileMode.Open, FileAccess.Read,
                FileShare.None);
                inUse = false;
            }
            catch
            {
            }
            finally
            {
                if (fs != null)
                    fs.Close();
            }
            return inUse;//true表示正在使用,false没有使用  
        }

步骤2:读AB文件为dtA和dtB

//读A文件
            readXmlToDt readxmlA = new readXmlToDt();
            DataTable dtA = new DataTable();
            dtA = readxmlA.read_forList(Main_frm.pathA);
//读B文件
            readXmlToDt readxmlB = new readXmlToDt();
            DataTable dtB = new DataTable();
            dtB = readxmlB.read_forList(Main_frm.pathB);

步骤3:比较dtA和dtB,将结果存入dtC

//比较AB,得到结果
            compareDt resultDt = new compareDt();
            DataTable dtC = new DataTable();
            dtC=resultDt.compare(dtA, dtB, Main_frm.attrSelected);

步骤4:将dtC导出到Excel文件并存入指定路径(此时并未上色)

//导出Excel
            exportDtToExcel ex = new exportDtToExcel();
            ex.dtToExcel(dtC, strsavePath);

步骤5:执行宏(上色)。第一次启用程序之前需要在Excel中勾选对VBA开发的信任选项,否则无法启用宏。所以在这里使用了一个抛出异常,如果不能正确启用宏,给出提示信息。

//执行宏
            try
            {
                colorExcel ce = new colorExcel();
                ce.colorExc(strsavePath);
            }
            catch
            {
                MessageBox.Show("您未开启宏信任,请在excel→文件→选项→信任中心→信任中心设置→宏设置→勾选“信任对VBA工程对象模型的访问”,然后重启程序", "提示信息",MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

函数整体代码如下:      这里有一点偷懒,进度条的值在各个阶段直接赋值了,以后有时间研究一下如何使用进度条。

#region 主要处理过程
        private void btnSave_Click(object sender, EventArgs e)
        {
            if (strsavePath == "")
            {//如果保存路径为空
                MessageBox.Show("请选择要保存的路径!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            //检查文件是否已被打开
            if (File.Exists(strsavePath))
            {
                bool op = IsFileInUse(strsavePath);
                if (op)
                {
                    MessageBox.Show("您要保存的文件被占用!\n请关闭文件或重新选择路径");
                    return;
                }
            }

            prgSave.Value = 0;
            if (lblExport.Visible == false) { lblExport.Visible = true; }//如果导出的标志不可见,将它可见
            if (picsSuccess.Visible == true) { picsSuccess.Visible = false; }//如果成功的标志可见,将它不可见
            prgSave.Value = 10;
            //读A文件
            readXmlToDt readxmlA = new readXmlToDt();
            DataTable dtA = new DataTable();
            dtA = readxmlA.read_forList(Main_frm.pathA);
            //读B文件
            readXmlToDt readxmlB = new readXmlToDt();
            DataTable dtB = new DataTable();
            dtB = readxmlB.read_forList(Main_frm.pathB);

            prgSave.Value = 30;
            //比较AB,得到结果
            compareDt resultDt = new compareDt();
            DataTable dtC = new DataTable();
            dtC=resultDt.compare(dtA, dtB, Main_frm.attrSelected);

            prgSave.Value = 90;
            //导出Excel
            exportDtToExcel ex = new exportDtToExcel();
            ex.dtToExcel(dtC, strsavePath);

            //进度条、进度标签、成功标志
            prgSave.Value = 100;
            lblExport.Visible = false;
            picsSuccess.Visible = true;

            //执行宏
            try
            {
                colorExcel ce = new colorExcel();
                ce.colorExc(strsavePath);
            }
            catch
            {
                MessageBox.Show("您未开启宏信任,请在excel→文件→选项→信任中心→信任中心设置→宏设置→勾选“信任对VBA工程对象模型的访问”,然后重启程序", "提示信息",MessageBoxButtons.OK, MessageBoxIcon.Error);
            }         
        }
#endregion
        public static bool IsFileInUse(string fileName)
        {//检测Excel文件是否被打开
            bool inUse = true;
                      FileStream fs = null;
            try
            {
                fs = new FileStream(fileName, FileMode.Open, FileAccess.Read,
                FileShare.None);
                inUse = false;
            }
            catch
            {
            }
            finally
            {
                if (fs != null)
                    fs.Close();
            }
            return inUse;//true表示正在使用,false没有使用  
        }

Excel文件保存后,再打开启用宏指令的文件其实是一个副本,用户如果选择将这个带颜色的文件保存那么原始的Excel文件就没有用了,因此点击完成图片后将它删除并关闭程序。这里如果不点击完成而是直接关掉程序,那么原始Excel文件就不会被删除。

private void picsSuccess_Click(object sender, EventArgs e)
        {
            Application.Exit();
            File.Delete(strsavePath);//删除文件
        }

 

3. readXmlToDt.cs:类文件。用于将xml文件读到一个dataTable中。

4.compareDt.cs:类文件。在两个dataTable所有列名相同的情况下,比较相同ID行的属性值是否相同。

5. exportDtToExcel.cs:类文件。将dataTable输出到Excel文件中。

6.colorExcel.cs:类文件。用C#在Excel文件中创建宏(VB),应用此宏来标识特定条件的文本。