【问题标题】:How do I read in a single column from an Excel spreadsheet?如何从 Excel 电子表格中读取单个列?
【发布时间】:2015-12-14 17:05:08
【问题描述】:

我正在尝试从 Excel 文档中读取单个列。我想阅读整列,但显然只存储有数据的单元格。我也想尝试处理列中的单元格为空的情况,但如果列中还有更远的内容,它将读取以后的单元格值。例如:

| Column1 |
|---------|
|bob      |
|tom      |
|randy    |
|travis   |
|joe      |
|         |
|jennifer |
|sam      |
|debby    |

如果我有那一列,我不介意在joe 之后的行的值是"",但我确实希望它在空白单元格之后继续获取值。但是,假设debby 是列中的最后一个值,我不希望它继续超过debby 35,000 行。

假设这将始终是第一列也是安全的。

到目前为止,我有这个:

Excel.Application myApplication = new Excel.Application();
myApplication.Visible = true;
Excel.Workbook myWorkbook = myApplication.Workbooks.Open("C:\\aFileISelect.xlsx");
Excel.Worksheet myWorksheet = myWorkbook.Sheets["aSheet"] as Excel.Worksheet;
Excel.Range myRange = myWorksheet.get_Range("A:A", Type.Missing);

foreach (Excel.Range r in myRange)
{
    MessageBox.Show(r.Text);
}

我从旧版本的 .NET 中找到了很多做类似事情的例子,但不完全是这样,我想确保我做了一些更现代的事情(假设人们用来做这件事的方法已经改变了一些)金额)。

我当前的代码读取整个列,但在最后一个值之后包含空白单元格。


EDIT1

我喜欢下面 Isedlacek 的回答,但我确实有一个问题,我不确定是否特定于他的代码。如果我以这种方式使用它:

Excel.Application myApplication = new Excel.Application();
myApplication.Visible = true;
Excel.Workbook myWorkbook = myApplication.Workbooks.Open("C:\\aFileISelect.xlsx");
Excel.Worksheet myWorksheet = myWorkbook.Sheets["aSheet"] as Excel.Worksheet;
Excel.Range myRange = myWorksheet.get_Range("A:A", Type.Missing);

var nonEmptyRanges = myRange.Cast<Excel.Range>()
.Where(r => !string.IsNullOrEmpty(r.Text));

foreach (var r in nonEmptyRanges)
{
    MessageBox.Show(r.Text);
}

MessageBox.Show("Finished!");

Finished! MessageBox 永远不会显示。我不确定为什么会发生这种情况,但它似乎从未真正完成搜索。我尝试在循环中添加一个计数器,以查看它是否只是在不断地搜索该列,但它似乎不是......它似乎只是停止了。

Finished! MessageBox 在哪里,我试图关闭工作簿和电子表格,但该代码从未运行(正如预期的那样,因为 MessageBox 从未运行)。

如果我手动关闭 Excel 电子表格,我会收到 COMException:

COMException 未被用户代码处理
附加信息:HRESULT 异常:0x803A09A2

有什么想法吗?

【问题讨论】:

  • 哈哈,这就是目标,谢谢!
  • 我的回答对您有帮助吗?为了解决您遇到的效率问题,我进行了多次更新。
  • 我的要求发生了变化,我以不同的方式做到了这一点,但我认为你的答案对于任何偶然发现这一点并需要解决与问题相同的问题的人来说都是很好的。感谢您的精彩回答!

标签: c# excel c#-4.0


【解决方案1】:

答案取决于您是要获取已使用单元格的边界范围,还是要从列中获取非空值。

以下是如何有效地从列中获取非空值的方法。请注意,一次读取整个 tempRange.Value 属性比逐个单元格读取要快MUCH,但代价是生成的数组会占用大量内存。

private static IEnumerable<object> GetNonNullValuesInColumn(_Application application, _Worksheet worksheet, string columnName)
{
    // get the intersection of the column and the used range on the sheet (this is a superset of the non-null cells)
    var tempRange = application.Intersect(worksheet.UsedRange, (Range) worksheet.Columns[columnName]);

    // if there is no intersection, there are no values in the column
    if (tempRange == null)
        yield break;

    // get complete set of values from the temp range (potentially memory-intensive)
    var value = tempRange.Value2;

    // if value is NULL, it's a single cell with no value
    if (value == null)
        yield break;

    // if value is not an array, the temp range was a single cell with a value
    if (!(value is Array))
    {
        yield return value;
        yield break;
    }

    // otherwise, the value is a 2-D array
    var value2 = (object[,]) value;
    var rowCount = value2.GetLength(0);
    for (var row = 1; row <= rowCount; ++row)
    {
        var v = value2[row, 1];
        if (v != null)
            yield return v;
    }
}

这是获取包含列中非空单元格的最小范围的有效方法。请注意,我仍在一次读取整个 tempRange 值集,然后使用生成的数组(如果是多单元格范围)来确定哪些单元格包含第一个和最后一个值。然后我在弄清楚哪些行有数据后构建边界范围。

private static Range GetNonEmptyRangeInColumn(_Application application, _Worksheet worksheet, string columnName)
{
    // get the intersection of the column and the used range on the sheet (this is a superset of the non-null cells)
    var tempRange = application.Intersect(worksheet.UsedRange, (Range) worksheet.Columns[columnName]);

    // if there is no intersection, there are no values in the column
    if (tempRange == null)
        return null;

    // get complete set of values from the temp range (potentially memory-intensive)
    var value = tempRange.Value2;

    // if value is NULL, it's a single cell with no value
    if (value == null)
        return null;

    // if value is not an array, the temp range was a single cell with a value
    if (!(value is Array))
        return tempRange;

    // otherwise, the temp range is a 2D array which may have leading or trailing empty cells
    var value2 = (object[,]) value;

    // get the first and last rows that contain values
    var rowCount = value2.GetLength(0);
    int firstRowIndex;
    for (firstRowIndex = 1; firstRowIndex <= rowCount; ++firstRowIndex)
    {
        if (value2[firstRowIndex, 1] != null)
            break;
    }
    int lastRowIndex;
    for (lastRowIndex = rowCount; lastRowIndex >= firstRowIndex; --lastRowIndex)
    {
        if (value2[lastRowIndex, 1] != null)
            break;
    }

    // if there are no first and last used row, there is no used range in the column
    if (firstRowIndex > lastRowIndex)
        return null;

    // return the range
    return worksheet.Range[tempRange[firstRowIndex, 1], tempRange[lastRowIndex, 1]];
}

【讨论】:

  • 谢谢,如果我最终有时间,我会将其移植到 C# 并将其添加为我的问题的编辑。我认为这样的事情会对发现这个问题的人有所帮助。
  • 我刚刚做了一些修改。我相信我已经完成了编辑。最终答案!
【解决方案2】:

如果您不介意完全丢失空行:

var nonEmptyRanges = myRange.Cast<Excel.Range>()
    .Where(r => !string.IsNullOrEmpty(r.Text))
foreach (var r in nonEmptyRanges)
{
    // handle the r
    MessageBox.Show(r.Text);
}

【讨论】:

  • 完美回答了这个问题,谢谢。并且不需要保留空行。
  • 我对此代码有疑问。如果我使用它,它似乎永远不会完成。我将对我的问题进行编辑以解释我的意思。
  • 这没有完成的原因是因为它正在评估工作表中的每个单元格(或列中的每个单元格,取决于 myRange 是什么)。在 Excel 2007+ 中,每列有 1,048,576 个单元格。 Excel 互操作速度非常慢。这就是为什么您需要采用我的回答中的技术来限制您评估的单元格数量。
【解决方案3】:
    /// <summary>
    /// Generic method which reads a column from the <paramref name="workSheetToReadFrom"/> sheet provided.<para />
    /// The <paramref name="dumpVariable"/> is the variable upon which the column to be read is going to be dumped.<para />
    /// The <paramref name="workSheetToReadFrom"/> is the sheet from which te column is going to be read.<para />
    /// The <paramref name="initialCellRowIndex"/>, <paramref name="finalCellRowIndex"/> and <paramref name="columnIndex"/> specify the length of the list to be read and the concrete column of the file from which to perform the reading. <para />
    /// Note that the type of data which is going to be read needs to be specified as a generic type argument.The method constraints the generic type arguments which can be passed to it to the types which implement the IConvertible interface provided by the framework (e.g. int, double, string, etc.).
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dumpVariable"></param>
    /// <param name="workSheetToReadFrom"></param>
    /// <param name="initialCellRowIndex"></param>
    /// <param name="finalCellRowIndex"></param>
    /// <param name="columnIndex"></param>
    static void ReadExcelColumn<T>(ref List<T> dumpVariable, Excel._Worksheet workSheetToReadFrom, int initialCellRowIndex, int finalCellRowIndex, int columnIndex) where T: IConvertible
    {
        dumpVariable = ((object[,])workSheetToReadFrom.Range[workSheetToReadFrom.Cells[initialCellRowIndex, columnIndex], workSheetToReadFrom.Cells[finalCellRowIndex, columnIndex]].Value2).Cast<object>().ToList().ConvertAll(e => (T)Convert.ChangeType(e, typeof(T)));
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多