none
关于用C#解析Excel的疑问 RRS feed

  • 问题

  • 读取了一个Excel,并使用下面语句:

    Range range = datasheet.get_Range("D5", "D10");
    System.Array values = (System.Array)range.Formula;

    我本来想让values的元素都是对应单元格的纯数据,但发现有些元素获取到的却是计算公式(也就是说有些数据是通过公式自动计算出来的)。

    Excel里面的数据格式有:整数,位数不等的小数,百分数,日期(如2012-3-3)。为了让数组values得到每个单元格的实际数据,而不是计算公式,如何用代码解决?

    ps:每个单元格只有一种数据格式。

    2012年3月8日 2:56

答案

  • 您好,請改用一個Cell一個Cell讀的方式!

    for (int i = 'A'; i <= 'Z'; i++)
    {
    	for (int j = 4; j <= 23; j++)
    	{
    		string idx = Encoding.Default.GetString(new byte[] { (byte)i }) + j.ToString();
    		object oneRange = xlWorkSheet.get_Range(idx).Text;
    		listBox1.Items.Add(idx + "=" + oneRange as string);
    	}
    }


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    2012年3月9日 5:36

全部回复

  • 您好,請問有試過使用range.Value嗎?

    object values = range.Value;


    以上說明若有錯誤請指教,謝謝。 亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    2012年3月8日 3:40
  • 您说的应该是object数组吧?

    object[] values = range.Value;

    是这样吗?

    2012年3月8日 4:42
  • 如果是这种写法,那么应该改为object[][] values = range.Value;吧?
    2012年3月8日 4:56
  • 您好,請問有試過使用range.Value嗎?

    object values = range.Value;


    以上說明若有錯誤請指教,謝謝。 亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    最近任务赶,为了这个主题我花了很多时间还是解决不了。我把相关的代码贴上来,麻烦您指导下如何改,感激不尽了!

    Range range = datasheet.get_Range("A4", "Z16");   //读取指定位置的表格数据;取A2 和B2 列的值,结果是 6  7(好像不是这么解释啊)
                                                                 //datasheet:数据表
                                                                 /*Range 对象表示以下内容:单元格、行、列、包含一个或多个单元格块的单元格集合,
                                                                  甚至还可以表示多个工作表上的一组单元格。*/

                System.Array values = (System.Array)range.Formula;   //range.Formula:range即是上一句定义的range
                                                                     /*range.Formula:excel的VBA语句。作用:返回用于计算显示值的公式
                                                                      自己翻译自:http://msdn.microsoft.com/zh-cn/library/ff838835 */
               
                if (values != null)
                {
                    int len1 = values.GetLength(0);   //Array.GetLength方法:获取一个 32 位整数,该整数表示指定维中的元素数
                    int len2 = values.GetLength(1);   //len1=1,len2=2
                   
                   
                   
                    for (int i = 1; i <= len1; i++)
                    {
                        for (int j = 1; j <= len2; j++)
                        {
                            if (values.GetValue(i, j).ToString().Length == 0)   //Array.GetValue方法:获取多维 Array 中指定位置的值
                                this.textBox1.Text += " ";   //textBox:表示 Windows 文本框控件
                            //此行相当于this.textBox1.Text=this.textBox1.Text+" "
                            //textBox.Text-->textBox控件的文本内容
                            //此行作用计时在开头添加一个空格" "
                            //Array.GetValue 方法:获取当前 Array 中指定元素的值

                            this.textBox1.Text += values.GetValue(i, j).ToString() + " ";
                        }
                    }
                }

    2012年3月8日 5:08
  • 您好,我寫了一個測試程式,是取Range的值,並將它Assign到ListBox之中,您參考一下,謝謝!

    Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
    excel.Visible = true;
    Microsoft.Office.Interop.Excel.Workbook xlWorkBook = excel.Workbooks.Open(@"f:\t1.xls");
    Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet;
    object[,] rangeValue = xlWorkSheet.get_Range("A4", "Z16").Value as object[,];
    for (int i = rangeValue.GetLowerBound(0); i <= rangeValue.GetUpperBound(0); i++)
    {
    	for (int j = rangeValue.GetLowerBound(1); j <= rangeValue.GetUpperBound(1); j++)
    	{
    		listBox1.Items.Add(i.ToString() + "," + j.ToString() + "=" + rangeValue[i, j] as string);
    	}
    }
    //... your other code...


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/


    • 已编辑 亂馬客 2012年3月8日 7:09
    • 已建议为答案 Neddy Ren 2012年3月12日 2:46
    2012年3月8日 7:09
  • 您好,我寫了一個測試程式,是取Range的值,並將它Assign到ListBox之中,您參考一下,謝謝!

    Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
    excel.Visible = true;
    Microsoft.Office.Interop.Excel.Workbook xlWorkBook = excel.Workbooks.Open(@"f:\t1.xls");
    Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet;
    object[,] rangeValue = xlWorkSheet.get_Range("A4", "Z16").Value as object[,];
    for (int i = rangeValue.GetLowerBound(0); i <= rangeValue.GetUpperBound(0); i++)
    {
    	for (int j = rangeValue.GetLowerBound(1); j <= rangeValue.GetUpperBound(1); j++)
    	{
    		listBox1.Items.Add(i.ToString() + "," + j.ToString() + "=" + rangeValue[i, j] as string);
    	}
    }
    //... your other code...


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/


    早上好!我按照您昨天的代码,添加后的整段是这样子的

    :using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Microsoft.Office.Interop.Excel;
    using System.Reflection;

    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)   //添加按钮事件
            {
                Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
                excel.Visible = true;
                Microsoft.Office.Interop.Excel.Workbook xlWorkBook = excel.Workbooks.Open(@"f:\t1.xls");
                Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet;
                object[,] rangeValue = xlWorkSheet.get_Range("A4", "Z16").Value as object[,];
                for (int i = rangeValue.GetLowerBound(0); i <= rangeValue.GetUpperBound(0); i++)
                {
                    for (int j = rangeValue.GetLowerBound(1); j <= rangeValue.GetUpperBound(1); j++)
                    {
                        listBox1.Items.Add(i.ToString() + "," + j.ToString() + "=" + rangeValue[i, j] as string);
                    }
                }

                excel.Quit();
                excel = null;      

            }
        }
    }
    但出现两个错误:

    1.无法将类型“object”隐式转换为“Microsoft.Office.Interop.Excel.Worksheet”。存在一个显式转换(是否缺少强制转换?)  

    2.当前上下文中不存在名称“listBox1”  

    您自己这样写没出错吗?是不是我少了什么代码?

    2012年3月9日 1:27
  • 您好,

    1.轉型請改成

    Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;

    2. listBox1是我拉到畫面上的listBox,您畫面上沒有的話,當然會說您要那個物件! 所以您可以拉一個listBox到畫面上,取名叫listBox1。

    Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
    excel.Visible = true;
    Microsoft.Office.Interop.Excel.Workbook xlWorkBook = excel.Workbooks.Open(@"f:\t1.xls");
    Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;
    object[,] rangeValue = xlWorkSheet.get_Range("A4", "Z16").Value as object[,];
    for (int i = rangeValue.GetLowerBound(0); i <= rangeValue.GetUpperBound(0); i++)
    {
    	for (int j = rangeValue.GetLowerBound(1); j <= rangeValue.GetUpperBound(1); j++)
    	{
    		listBox1.Items.Add(i.ToString() + "," + j.ToString() + "=" + rangeValue[i, j] as string);
    	}
    }
    
    excel.Quit();
    excel = null; 
    我的測試範例:Example


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    • 已建议为答案 Neddy Ren 2012年3月12日 2:45
    2012年3月9日 2:55
  • 您好,

    1.轉型請改成

    Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;

    2. listBox1是我拉到畫面上的listBox,您畫面上沒有的話,當然會說您要那個物件! 所以您可以拉一個listBox到畫面上,取名叫listBox1。

    Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
    excel.Visible = true;
    Microsoft.Office.Interop.Excel.Workbook xlWorkBook = excel.Workbooks.Open(@"f:\t1.xls");
    Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet = xlWorkBook.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;
    object[,] rangeValue = xlWorkSheet.get_Range("A4", "Z16").Value as object[,];
    for (int i = rangeValue.GetLowerBound(0); i <= rangeValue.GetUpperBound(0); i++)
    {
    	for (int j = rangeValue.GetLowerBound(1); j <= rangeValue.GetUpperBound(1); j++)
    	{
    		listBox1.Items.Add(i.ToString() + "," + j.ToString() + "=" + rangeValue[i, j] as string);
    	}
    }
    
    excel.Quit();
    excel = null; 
    我的測試範例:Example


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    您好,您给我的附件我收到了。真的很感谢!因为您这种方法我没写过,而且这边出现了一些档案方面的问题,所以还有两个疑问:

    1.如何添加代码使它有打开对应名字的sheet?

    2.请原谅我之前没把一些地方点清楚-->Excel单元格的数据形式有这几种:百分数,实数(整数和小数),还有一种如 #DIV/0!(后面的感叹号原本有的)

    怎么才能按照原本的形式显示出来?(就比如说百分数的还是显示为百分数,这样子)

    2012年3月9日 4:02
  • dear

    应该用Value或Value2属性来取得该除存格的资料,在VBA或是C#里就先直接取得那个除存格的结果

    另外有些操作你可参考以下,用来写入大量资料用,适用Excel2003版本

    http://www.dotblogs.com.tw/yc421206/archive/2009/01/11/6727.aspx

    //設定範圍
    myRange=(Range)mySheet.get_Range(myExcel.Cells[2, 1], myExcel.Cells[UpBound1 + 1, UpBound2 + 1]);
    myRange.Select();
    //用陣列一次寫入資料
    myRange.Value2 = "'" + myData;

    关于Value2的用法参考

    http://support.microsoft.com/kb/213719/zh-tw

    除了用Excel VBA本身直接控制Excel效率比较好之外,用其他外部语言操作Excel效率都狠差,尤其是分析除存格的廻圈,所以一次读回/写入Range的效率,会比分析除存格的廻圈还要好


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    2012年3月9日 4:34
  • dear

    应该用Value或Value2属性来取得该除存格的资料,在VBA或是C#里就先直接取得那个除存格的结果

    另外有些操作你可参考以下,用来写入大量资料用,适用Excel2003版本

    http://www.dotblogs.com.tw/yc421206/archive/2009/01/11/6727.aspx

    //設定範圍
    myRange=(Range)mySheet.get_Range(myExcel.Cells[2, 1], myExcel.Cells[UpBound1 + 1, UpBound2 + 1]);
    myRange.Select();
    //用陣列一次寫入資料
    myRange.Value2 = "'" + myData;

    关于Value2的用法参考

    http://support.microsoft.com/kb/213719/zh-tw

    除了用Excel VBA本身直接控制Excel效率比较好之外,用其他外部语言操作Excel效率都狠差,尤其是分析除存格的廻圈,所以一次读回/写入Range的效率,会比分析除存格的廻圈还要好


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/

    value或value2试过了,百分数的显示为小数,不是我要的原样格式。其他格式的还没尝试。
    2012年3月9日 5:06
  • 我试过Range.Value和Range.Value2,实数形式的可以按原本显示,但是遇到如27%这种的,显示出来的只有

    小数。Excel单元格的数据形式有这几种:百分数,实数(整数和小数),还有一种如 #DIV/0!(后面的感叹号原本有的)

    怎么才能按照原本的形式显示出来?

    2012年3月9日 5:18
  • 您好,請改用一個Cell一個Cell讀的方式!

    for (int i = 'A'; i <= 'Z'; i++)
    {
    	for (int j = 4; j <= 23; j++)
    	{
    		string idx = Encoding.Default.GetString(new byte[] { (byte)i }) + j.ToString();
    		object oneRange = xlWorkSheet.get_Range(idx).Text;
    		listBox1.Items.Add(idx + "=" + oneRange as string);
    	}
    }


    以上說明若有錯誤請指教,謝謝。
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    2012年3月9日 5:36
  • dear

    像乱马兄讲的用Text属性就能取得你要的效果Value/Value2都是内部的值,而Text属性是Excel设定的样式显示结果,另外Execl并不会主动回收,在工作管理员会看到EXCEL.exe,等到天荒地老她还在那边,除非主进程关闭,若进程发生例外,可想而知他会卡在那里不动,

    你必须确认所有工作都做完不会在用到EXCEL就可以回收他,并在try/finally确认他有被回收,你可以参考以下连结及写法

    http://www.dotblogs.com.tw/yc421206/archive/2009/07/16/9553.aspx

    http://www.dotblogs.com.tw/yc421206/archive/2012/03/09/70624.aspx

    using System;
    using System.Windows.Forms;
    using Excel = Microsoft.Office.Interop.Excel;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            Excel.Application _excel = null;
            private void button1_Click(object sender, EventArgs e)
            {
                try
                {
                    initailExcel();
                    readExcel();
                }
                catch (Exception ex)
                {
                    //TODO:記錄錯誤
                }
                finally
                {
                    _excel.Quit();
                    _excel = null;
                    GC.Collect();
                }
    
            }
            void initailExcel()
            {
                this._excel = new Excel.Application();
                this._excel.Visible = true;//設false效能會比較好
            }
            void readExcel()
            {
                Excel.Workbook book = _excel.Workbooks.Open(Application.StartupPath + "\\test.xls");
                Excel.Worksheet sheet = book.Sheets[1];//指定活頁簿
                Excel.Range range = sheet.get_Range("A5", "D23");
                range.Select();//可以不要用
                foreach (Excel.Range item in range)
                {
                    Console.WriteLine(item.Cells.Formula);
                    Console.WriteLine(item.Cells.Value);
                    Console.WriteLine(item.Cells.Value2);
                    Console.WriteLine(item.Cells.Text);//你要的效果
                }
    
                book.Close();
                book = null;
            }
            private void button2_Click(object sender, EventArgs e)
            {
                //確認已經沒有excel工作再回收
                GC.Collect();
            }
        }
    }


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/




    2012年3月9日 7:22
  • 真的很感谢两位对我的耐心指导!这两天有点忙,代码我明天再调试下。
    2012年3月11日 2:25