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

时间:2015-03-05 10:38:44

标签: c# excel openxml

我一直在寻找一段时间,似乎无法找到如何做到这一点。 我有一张excel表,我正在使用OpenXML阅读。现在正常情况是循环遍历行然后循环遍历单元格以获取值,这很好。但是随着值的增加,我需要单元格的位置,格式为(rowindex,ColumnIndex)。我设法获得了rowIndex,但似乎无法找到索引列。

我实际上认为这很容易,但显然不是。

8 个答案:

答案 0 :(得分:19)

这比您想象的要复杂一些,因为架构允许省略空单元格。

要获取索引,您可以使用Cell对象,其中CellReference属性以A1B1等格式提供引用。您可以使用引用提取列号。

正如您可能知道的那样,在Excel A = 1B = 2等等Z = 26之前,单元格以A为前缀,以提供AA = 27AB = 28等。请注意,在AA的情况下,第一个A的值为第二个的{26}。即它值得" 26,而第二个A是"值得" 1总共27个。

要计算列索引,您可以反转字母,然后取第一个字母的值并将其添加到运行总计中。然后取第二个字母的值并将其乘以26,将总数加到第一个数字。对于第三个,你将它乘以26两次并加上它,第四个乘以26 3次,依此类推。

因此,对于列ABC,您可以这样做:

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

在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;
}

请注意,CellReference 保证在XML中(尽管我从未见过它)。在CellReference为空的情况下,单元格被放置在最左边的可用单元格中。 RowIndex在规范中也不是必需的,因此它也可以省略,在这种情况下,单元格被放置在可用的最高行中。更多信息可以在this question中看到。在CellReferencenull的情况下,来自@BCdotWEB的answer是正确的方法。

答案 1 :(得分:7)

小是美丽的

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;
}

答案 2 :(得分:3)

要开始回答,我邀请您先查看this

正如我所解释的, NO 提取行和列的简便方法。您得到的最接近的是提取CellReference一个单元格,其格式为A1B2,格式为COLUMN_ROW格式。

您可以做的是从CellReference中提取行和列。是的,这需要您实施一种方法,您需要通过char检查char以验证数字和字符串。

假设您有A11,那么当您需要索引列时,您需要提取A,这将提供column 1。是的,这并不容易,但除非您在扫描/遍历单元格时选择对列进行计数,否则它是唯一的方法。

再次查看this问题答案,它们做同样的事情。

答案 3 :(得分:3)

    [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 );

答案 4 :(得分:1)

    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]);
                        }
        }

这是为了找到现有/使用的列总数 enter image description here

答案 5 :(得分:0)

在我的场景中,我只需要处理列名(没有单元格编号),并使用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

要还原,以及完整的代码和测试,请参阅this要点。

答案 6 :(得分:0)

在@petelids答案中稍作修改的 GetColumnIndex 函数。结果将是从零开始的索引。如果需要,为基于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;
}

答案 7 :(得分:0)

    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);
            }
        }
    }