简体   繁体   English

使用 OpenXML C# 获取 Excel 中单元格的列索引

[英]Get the Column Index of a Cell in Excel using OpenXML C#

I've been looking around for a while now and cannot seem to find out how to do this.我一直在环顾四周,似乎无法找到如何做到这一点。 I've got an excel sheet, which I'm reading using OpenXML.我有一张 excel 表,我正在使用 OpenXML 阅读它。 Now the normal thing would be to loop through the rows and then loop through the cells to get the values, which is fine.现在正常的做法是遍历行,然后遍历单元格以获取值,这很好。 But along with the values I need the location of the cell, which would be in the format (rowindex, ColumnIndex).但是除了值之外,我还需要单元格的位置,格式为(rowindex,ColumnIndex)。 I've managed to get the rowIndex, but cant seem to figure out getting the column Index.我设法获得了rowIndex,但似乎无法弄清楚获得列索引。

I actually thought this was going to be easy but apparently it isnt.我实际上认为这很容易,但显然不是。

This is slightly trickier than you might imagine because the schema allows for empty cells to be omitted.这比您想象的要复杂一些,因为架构允许省略空单元格。

To get the index you can use the Cell object wihch has a CellReference property that gives the reference in the format A1 , B1 etc. You can use that reference to extract the column number.要获取索引,您可以使用具有CellReference属性的Cell对象,该属性以A1B1等格式提供引用。您可以使用该引用来提取列号。

As you probably know, in Excel A = 1 , B = 2 etc up to Z = 26 at which point the cells are prefixed with A to give AA = 27 , AB = 28 etc. Note that in the case of AA the first A has a value of 26 times the second;您可能知道,在 Excel A = 1B = 2等直到Z = 26时,单元格以A为前缀以给出AA = 27AB = 28等。请注意,在AA的情况下,第A值为秒的 26 倍; ie it is "worth" 26 whilst the second A is "worth" 1 giving a total of 27.即它“价值”26,而第二个A “价值”1,总共27。

To work out the column index you can reverse the letters then take the value of the first letter and add it to a running total.要计算列索引,您可以反转字母,然后取第一个字母的值并将其添加到运行总数中。 Then take the value of the second letter and multiply it by 26, adding the total to the first number.然后将第二个字母的值乘以 26,将总数与第一个数字相加。 For the third you multiply it by 26 twice and add it, for the fourth multiply it by 26 3 times and so on.对于第三个,您将其乘以 26 两次并相加,对于第四个,将其乘以 26 3 次,依此类推。

So for column ABC you would do:因此,对于ABC列,您将执行以下操作:

C = 3
B = 2 * 26 = 52
A = 1 * 26 *26 = 676
3 + 52 + 676 = 731

In C# the following will work:在 C# 中,以下将起作用:

private static int? GetColumnIndex(string cellReference)
{
    if (string.IsNullOrEmpty(cellReference))
    {
        return null;
    }

    //remove digits
    string columnReference = Regex.Replace(cellReference.ToUpper(), @"[\d]", string.Empty);

    int columnNumber = -1;
    int mulitplier = 1;

    //working from the end of the letters take the ASCII code less 64 (so A = 1, B =2...etc)
    //then multiply that number by our multiplier (which starts at 1)
    //multiply our multiplier by 26 as there are 26 letters
    foreach (char c in columnReference.ToCharArray().Reverse())
    {
        columnNumber += mulitplier * ((int)c - 64);

        mulitplier = mulitplier * 26;
    }

    //the result is zero based so return columnnumber + 1 for a 1 based answer
    //this will match Excel's COLUMN function
    return columnNumber + 1;
}

Note that the CellReference is not guaranteed to be in the XML either (although I've never seen it not there).请注意, CellReference不能保证在 XML 中(尽管我从未见过它不在那里)。 In the case where the CellReference is null the cell is placed in the leftmost available cell.CellReference为空的情况下,单元格被放置在最左边的可用单元格中。 The RowIndex is also not mandatory in the spec so it too can be omitted in which case the cell is placed in the highest row available. RowIndex在规范中也不是强制性的,因此它也可以省略,在这种情况下,单元格放置在可用的最高行中。 More information can be seen in this question .更多信息可以在这个问题中看到。 The answer from @BCdotWEB is correct approach in cases where the CellReference is null .CellReferencenull情况下, CellReference答案是正确的方法。

Small is beautifull小就是美

int ColumnIndex(string reference)
{
  int ci=0;
  reference=reference.ToUpper();
  for (int ix = 0; ix < reference.Length && reference[ix] >= 'A';ix++ ) 
       ci = (ci * 26) + ((int)reference[ix] - 64);
  return ci;
}
    [TestCase( 1, 0, "A1" )]
    [TestCase( 2, 25, "Z2" )]
    [TestCase( 2, 38, "AM2" )]
    [TestCase( 2, (26 * 4) + 1, "DB2" )]
    [TestCase( 2, (26 * 26 * 26 * 18) + (26 * 26 * 1) + (26 * 26 * 1) + ( 26 * 1 ) + 2, "RBAC2" )]
    public void CanGetCorrectCellReference( int row, int column, string expected )
        => GetCellReference( (uint)row, (uint)column ).Value.ShouldEqual( expected );

    public static StringValue GetCellReference( uint row, uint column ) =>
        new StringValue($"{GetColumnName("",column)}{row}");

    static string GetColumnName( string prefix, uint column ) => 
        column < 26 ? $"{prefix}{(char)( 65 + column)}" : 
        GetColumnName( GetColumnName( prefix, ( column - column % 26 ) / 26 - 1 ), column % 26 );

To start answer , I invite you to look at this first.为了开始回答,我邀请你先看看这个

As I have explained there is NO easy way to extract Row and Column.正如我所解释的,没有简单的方法来提取行和列。 The closest you get is the extraction of CellReference of a cell which would have the form of A1 , B2 which is actualy COLUMN_ROW format.您得到的最接近的是提取单元格的CellReference ,该单元格的形式为A1B2 ,实际上是COLUMN_ROW格式。

What you can do is extract Row and Column from the CellReference .您可以做的是从CellReference提取 Row 和 Column 。 Yes this would need you to implement a method where you need to check char by char to verify for numbers and strings.是的,这需要你来实现,你需要检查的方法char通过char来验证数字和字符串。

Lets say you have A11 , then when you need to index column you need to extract A which would give as column 1 .假设您有A11 ,那么当您需要索引列时,您需要提取A ,它将作为column 1 Yes it's not that easy, but it's the only way unless you simply chose to count the columns when you scan/iterate through cells.是的,这并不容易,但这是唯一的方法,除非您在扫描/迭代单元格时简单地选择计算列数。

Again look at this questions answer which does the same thing.再次看看这个问题的答案,它做同样的事情。

    Row row = worksheetPart.Worksheet.GetFirstChild<SheetData>().Elements<Row>().FirstOrDefault();
   var totalnumberOfColumns = 0;
    if (row != null)
        {
            var spans = row.Spans != null ? row.Spans.InnerText : "";
                if (spans != String.Empty)
                        {
                            //spans.Split(':')[1];
                            string[] columns = spans.Split(':');
                            startcolumnInuse = int.Parse(columns[0]);
                            endColumnInUse = int.Parse(columns[1]);
                            totalnumberOfColumns = int.Parse(columns[1]);
                        }
        }

this is to find the total number of columns present/used这是为了找到存在/使用的列总数在此处输入图片说明

In my scenario I only needed to deal with column names (no cell numbers), and used LINQ, thought it's worth putting here for the reference.在我的场景中,我只需要处理列名(没有单元格编号),并使用了 LINQ,认为值得放在这里以供参考。

const int AsciiTrim = 'A' - 1; //64
const int LastChar = 'Z' - AsciiTrim; //26

var colIndex = columnName
    .Reverse()
    .Select(ch => ch - AsciiTrim)
    .Select((ch, i) => ch * Math.Pow(LastChar, i))
    .Sum()
    - 1; //make zero-index based

To revert back, and for the full code and test, see this gist.要恢复原状,以及完整的代码和测试,请参阅要点。

Slightly modified GetColumnIndex function in the @petelids answer.在@petelids 答案中稍微修改了GetColumnIndex函数。 Result will be zero-based index.结果将是从零开始的索引。 If need add 1 for a one-based Index.如果需要为基于 1 的索引添加 1。

private static int CellReferenceToIndex(string reference)
{
    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;
}
    public static void CellReferenceToIndex(string reference, out int row_index, out int col_index)
    {
        row_index = 0;
        col_index = 0;

        foreach(char c in reference)
        {
            if (c >= '0' && c <= '9')
            {
                row_index = row_index * 10 + (c - '0');
            }
            if (c >= 'A' && c <= 'Z')
            {
                col_index = col_index * ('Z' - 'A' + 1) + (c - 'A' + 1);
            }
        }
    }
private double CellReferenceToIndex(Cell cell)
    {
        // if Cell is ABC4 => position is
        // = [Aindx * (26^2)] + [BIndx * (27^1)] + [CIndx * (27^0)]
        // = [1     * (26^2)] + [2     * (27^1)] + [3     * (27^0)]

        double index = 0;
        char [] reference = cell.CellReference.ToString().ToUpper().Reverse().ToArray();
        int letterPosition = 0;
       
        foreach (char ch in reference)
        {
            if (char.IsLetter(ch))
            {
                int value = (ch - 'A') + 1; // so A is 1 not 0 
                index += value * Math.Pow(26, letterPosition++);
            }
        }
        return index;
    }

Just to add a new approach to this old question, I use this as a quick method to get the column index for a cell in a row (assuming you're looping through the Cells in a Row in SheetData, as the OP indicates they were).只是为了为这个老问题添加一种新方法,我使用它作为一种快速方法来获取一行中单元格的列索引(假设您正在循环通过 SheetData 中的一行中的单元格,因为 OP 表明它们是)。

You can use the ElementsBefore enumerable to count the Cells before the one you're currently looping on, and since that Count is one-based and Element IEnumerables are zero-based, using the Count will give you the column index of the Cell you're currently on (essentially, ElementsBefore + 1 = the current cell's column index).您可以使用 ElementsBefore 枚举来计算您当前循环的单元格之前的单元格,并且由于该 Count 是从一开始的,而 Element IEnumerables 是从零开始的,因此使用 Count 将为您提供单元格的列索引。当前重新打开(本质上,ElementsBefore + 1 = 当前单元格的列索引)。

So, something like this...所以,像这样的事情......

            For Each r In sht.Elements(Of Row)
                For Each c In sht.Elements(Of Row).ElementAt(r.RowIndex).Elements(Of Cell)
                    Dim iColumnIndex = c.ElementsBefore.Count
                Next
            Next

Some of the examples on this thread didn't work beyond Z.该线程上的一些示例在 Z 之后无法使用。

When validating, it's better to perform some Unit tests to confirm the column index is calculated correctly.验证时,最好执行一些单元测试以确认列索引计算正确。

Assuming the index count begins from 1, the following can be a useful reference...假设索引计数从 1 开始,以下可能是有用的参考...

Excel字母索引计算

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在C#中使用OpenXML读取Excel的空白单元格列值 - How to read blank cell column value of excel using OpenXML in C# 如何使用OpenXML库和C#获取Excel文档中当前选定的单元格或范围 - How to get currently selected cell or range in Excel document using OpenXML library and C# 如何使用 DocumentFormat.OpenXML 或 ClosedXML C# 从 Excel 获取合并单元格 - How to get a merged cell from Excel using DocumentFormat.OpenXML or ClosedXML C# 如何在 C# 中使用 OpenXML 将 RTF/HTML 放入 Excel 单元格 - How to put RTF/HTML into Excel cell using OpenXML in C# 在C#中使用OpenXml在Excel中创建日期单元格中的问题 - Issue in Creating Date cell in Excel using OpenXml in C# 使用行索引和列索引C#获取单元格值 - Get cell value using row index and column index c# 如何使用DocumentFormat.OpenXml C#获取excel工作表中的特定列? - How can I get the specific column in excel worksheet using DocumentFormat.OpenXml C#? 如何使用带有c#的openxml(不是整个行/列)隐藏和保护excel文件中一个Cell的文本? - How can I hide and protect the text of one Cell in an excel file using openxml with c# (not the entire row/ column)? 如何使用OpenXML和C#在Excel工作表中获取范围 - how to get range in excel sheet using openxml and c# 使用C#在新列中获取Excel单元格的缩进 - Get Indent of Excel Cell in new column using C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM