【问题标题】:Read a huge Excel column from C#从 C# 读取一个巨大的 Excel 列
【发布时间】:2019-07-08 18:32:07
【问题描述】:

我有一个包含四列的 Excel(可能是 2010 或 2013 不知道以后会不会出现问题)文档。前三列存储电话号码,它们基本上是一个包含 10 个或更多字符的字符串。仅四列将永远存储 1、2、3 或 4,它是一个类别。我需要检查列 A 中的每个数字是否出现在列 BC 中,所以我认为阅读每个数字的所有 Excel 单元格列并存储在列表中(尚未实现,因为我将在下面解释这个问题)。为此,我编写了以下代码:

private void btnCargarExcel_Click(object sender, EventArgs e)
        {
            if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
            {

                if (System.IO.File.Exists(openFileDialog1.FileName))
                {
                    filePath.Text = openFileDialog1.FileName.ToString();

                    Excel.Application xlApp;
                    Excel.Workbook xlWorkBook;
                    Excel.Worksheet xlWorkSheet;
                    Excel.Range range;

                    string str;

                    int rCnt = 0;

                    xlApp = new Microsoft.Office.Interop.Excel.Application();
                    xlWorkBook = xlApp.Workbooks.Open(openFileDialog1.FileName, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
                    xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

                    range = xlWorkSheet.UsedRange;

                    for (rCnt = 1; rCnt <= range.Rows.Count; rCnt++)
                    {
                        str = (range.Cells[rCnt, 1] as Excel.Range).Value2.ToString();
                        //bd.Add(cleanString(str));

                        bd.Add(cleanString(str, 10));
                    }

                    for (rCnt = 1; rCnt <= range.Rows.Count; rCnt++)
                    {
                        str = (range.Cells[rCnt, 2] as Excel.Range).Value2.ToString();
                        //bd.Add(cleanString(str));

                        bl.Add(cleanString(str, 10));
                    }

                    for (rCnt = 1; rCnt <= range.Rows.Count; rCnt++)
                    {
                        str = (range.Cells[rCnt, 3] as Excel.Range).Value2.ToString();
                        //bd.Add(cleanString(str));

                        cm.Add(cleanString(str, 10));
                    }

                    nrosProcesados.Text = bd.Count().ToString();
                    listBox1.DataSource = bd;

                    noProcesadosBL.Text = bl.Count().ToString();
                    listBox2.DataSource = bl;

                    noProcesadosCM.Text = cm.Count().ToString();
                    listBox3.DataSource = cm;

                    xlWorkBook.Close(true, null, null);
                    xlApp.Quit();

                    releaseObject(xlWorkSheet);
                    releaseObject(xlWorkBook);
                    releaseObject(xlApp);
                }
                else
                {
                    MessageBox.Show("No se pudo abrir el fichero!");
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(appExcel);
                    appExcel = null;
                    System.Windows.Forms.Application.Exit();
                }
            }
        }

所以我在列中迭代槽单元格,并在进行一些字符串更改后将每个数字存储在列表中,正如您在代码中看到的那样。这里的问题是A列有797340个单元格,B列有91617个单元格,C列有95891个单元格,所以如果我运行应用程序,加载Excel并等待我的PC挂出(即使有12GB的RAM和Core i3处理器)我需要打开任务管理器并结束任务。为了得到我想要的东西(只留下不重复的数字)而不是挂出我的电脑,最好的解决方案是什么?每个周期都可以在单独的线程中划分事情(我对此不太了解,因为我从 C# 开始,所以任何帮助将不胜感激)?你对这个话题有什么看法?

编辑:添加一个新的干净的方法

所以在阅读和阅读并从这里的一些成员那里获得帮助后,我对代码进行了一些改进,但现在我遇到了另一个问题(在代码下方注释)。立即查看代码:

// this goes first when I declare vars
public static System.Array objRowAValues;

// this goes in action when I click the button (I leave only relevant part)
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
Excel.Range range, rngARowLast;

string str;
int rCnt = 0;

long lastACell, fullRow;

xlApp = new Microsoft.Office.Interop.Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(openFileDialog1.FileName, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xlWorkSheet = (Excel.Worksheet) xlWorkBook.Worksheets.get_Item(1);

range = xlWorkSheet.UsedRange;

fullRow = xlWorkSheet.Rows.Count;
lastACell = xlWorkSheet.Cells[fullRow, 1].End(Excel.XlDirection.xlUp).Row;
rngARowLast = xlWorkSheet.get_Range("A1", "A" + lastACell);
objRowAValues = (System.Array) rngARowLast.Cells.Value;

现在因为我将使用来自 objRowAValues 的值填充 ListBox,而 ListBox 只接受 List 作为 DataSource,所以我需要将 objRowAValues 转换为字符串列表。我尝试this,但它对我不起作用。有什么帮助吗?

【问题讨论】:

  • 对不起,这种方法有一个完全的缺陷:它很慢。要解决这个问题:从 Excel 加载整个列 - 这速度要快得多,尤其是在大数据集上。它就像这个线程:stackoverflow.com/questions/536636/write-array-to-excel-range 如果您只有 xlsx 文档,请使用 EPPLUS 等第三方库来读取数据。它要快得多。
  • 将每列的值直接存储在数组中(不循环列中的每个单元格),然后对数组执行操作。 SO 有许多链接向您展示如何执行此操作。
  • @ChristianSauer 谢谢,但我不明白如何从 Excel 加载整个列,除非你在谈论我不太理解的第一个和接受的回复,所以一些代码会很有帮助对我来说;)
  • @ChristianSauer 我找到了this,但不知道如何在我的代码中使用,因为他们使用System.Array,而我正在使用List&lt;string&gt; bd = new List&lt;string&gt;();,有什么帮助吗?
  • 无需害怕 VBA。如果这是更大应用程序的一部分,您可以在 C# 中启动宏。

标签: c# excel multithreading


【解决方案1】:

不幸的是,我更像是一个 VB.NET 人 - 所以我已经为你转换了一些代码。 我希望这是开箱即用的——我在这里不使用这种工具,所以我无法测试它。

public void test()
{
    object[,] RaWData = null;

    dynamic range = xlWorkSheet.UsedRange;

    //i am unsure here about the correct order - I do not work with excel at Work, so you might have to change the following lange, if columns needs to be before rows or so
    RaWData = range.value2;

    //I am using a list here, because Lists are a lot easier to work with then simple arrays
    List<List<string>> RealData = new List<List<string>>();

    //start at 1  because the excel-delivered array do not have values at index 0 - this is the only 1-based array you will ever encounter in .net
    for (x = 1; x <= Information.UBound(RaWData, 1); x++) {
        List<string> templist = new List<string>();
        for (y = 1; y <= Information.UBound(RaWData, 2); y++) {
            templist.Add(RaWData[x, y].ToString());
        }
        RealData.Add(templist);
    }

    //you should be finished here...
}

【讨论】:

  • 感谢@christian-sauer,但我不知道如何将代码从 VB.NET 移动到 C# :-(,你知道如何做同样的事情,但改用 VBA 吗?
  • 这是 C# - 我已经为你转换了它......作为 VBA 它没有任何意义 - 因为重要的部分在 vba 中完全没用。
  • 你说得对,我没看错(所以我为你转换了一些代码)。现在可以添加一些方法来获取 C# 解析文件所需的时间吗?
  • @chistian-sauer 你能看看我之前对主帖所做的编辑吗?
猜你喜欢
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 2016-11-26
  • 2016-05-06
  • 1970-01-01
  • 2021-05-29
相关资源
最近更新 更多