【问题标题】:How to skip empty cells while reading data from Excel using OpenXML?使用 OpenXML 从 Excel 读取数据时如何跳过空单元格?
【发布时间】:2021-10-18 06:43:21
【问题描述】:

我正在尝试从 Excel 中读取数据并使用 OpenXML 将其存储到 DataTable 中。我希望 DataTable 中的数据与 Excel 工作表中的数据一样,但是当 Excel 中有一个空单元格时,它看起来并不像预期的那样。

因为代码row.Descendants<Cell>().ElementAt(i) 在读取数据时会跳过空单元格,并且在 DataTable 中的行和列存储不正确。我使用下面的代码解决了这个问题,但是当我的 excel 有超过 26 列时,它不能按预期工作,并且数据再次错误地存储在 DataTable 中。 (即,从 AA、AB、AC 列读取数据时)

当列数超过 26 列时,谁能帮我重写这段代码来处理这个问题。

    private static int CellReferenceToIndex(Cell cell)
    {
        int index = 0;
        string reference = cell.CellReference.ToString().ToUpper();
        foreach (char ch in reference)
        {
            if (Char.IsLetter(ch))
            {
                int value = (int)ch - (int)'A';
                index = (index == 0) ? value : ((index + 1) * 26) + value;
            }
            else
            {
                return index;
            }
        }
        return index;
    }

【问题讨论】:

  • @Jazb - 我尝试了这个答案,但是当整列为空时,它无法正常工作。例如,在excel中,如果AB列是空的,当它读取AC时,它正在跳过B列中的值。我想说的是,如果单元格为空,但如果整个列本身为空,则该解决方案无法按预期工作。

标签: c# excel openxml


【解决方案1】:

您可以使用下面的示例(取自 here 并通过少量验证进行了改进):

public static int GetColumnIndex(this Cell cell)
{
    string columnName = string.Empty;

    if (cell != null)
    {
        string cellReference = cell.CellReference?.ToString();

        if (!string.IsNullOrEmpty(cellReference))
            // Using `Regex` to "pull out" only letters from cell reference
            // (leave only "AB" column name from "AB123" cell reference)
            columnName = Regex.Match(cellReference, @"[A-Z]{1,3}").Value;
    }
 
    // Column name validations (not null, not empty and contains only UPPERCASED letters)
    // *uppercasing may be done manually with columnName.ToUpper()
    if (string.IsNullOrEmpty(columnName))
        throw new ArgumentException("Column name was not defined.", nameof(columnName));
    else if (!Regex.IsMatch(columnName, @"^[A-Z]{1,3}$"))
        throw new ArgumentException("Column name is not valid.", nameof(columnName));

    int index = 0;
    int pow = 1;

    // A - 1 iteration, AA - 2 iterations, AAA - 3 iterations.
    // On each iteration pow value multiplies by 26
    // Letter number (in alphabet) + 1 multiplied by pow value
    for (int i = columnName.Length - 1; i >= 0; i--)
    {
        index += (columnName[i] - 'A' + 1) * pow;
        pow *= 26;
    }

    // Index couldn't be greater than 16384
    if (index >= 16384)
        throw new IndexOutOfRangeException("Index of provided column name (" + index + ") exceeds max range (16384).");

    return index;
}

如果您有日志记录,您可以用return -1 和某种Log("...") 替换所有异常抛出。否则您可能无法确定发生了什么问题以及返回-1 的原因。

用法很明显:

var cells = row.Descendants<Cell>();

foreach (Cell cell in cells)
{
    int columnIndex = cell.GetColumnIndex();
    // Do what you want with that
}

编辑。

我不确定你想要达到什么目的。你的意思是:

因为代码row.Descendants&lt;Cell&gt;().ElementAt(i) 会跳过空单元格...

我没看到。看下面的例子:

介于 0 和 Descendants&lt;Cell&gt;().Count() 之间的随机 ElementAt 也可以工作,并显示空单元格和非空单元格:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-06
    • 1970-01-01
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多