web-dev-qa-db-ja.com

OpenXML C#を使用してExcelでセルの列インデックスを取得する

私はしばらく探し回っていましたが、これを行う方法を見つけることができないようです。私はOpenXMLを使用して読んでいるExcelシートを持っています。通常は、行をループしてからセルをループして値を取得しますが、これは問題ありません。しかし、値と共に、セルの場所が必要です。これは、フォーマット(rowindex、ColumnIndex)になります。私はなんとかrowIndexを取得しましたが、列Indexを取得することを理解できないようです。

これは簡単だと思っていましたが、明らかに簡単ではありません。

15
QV1

スキーマでは空のセルを省略できるため、これは想像よりも少し注意が必要です。

インデックスを取得するには、 Cell オブジェクトを使用できます CellReference プロパティがあり、A1B1などの形式で参照を提供しますこの参照を使用して、列番号を抽出できます。

おそらくご存知のとおり、ExcelではA = 1B = 2など、Z = 26までのセルがAで始まり、AA = 27AB = 28などになります。AAの場合、最初のAの値は秒の26倍です。つまり、2番目のAが「価値がある」1であるのに26は「価値がある」26で、合計で27になります。

列インデックスを計算するには、文字を逆にして最初の文字の値を取得し、それを現在の合計に追加します。次に、2番目の文字の値を取得し、26を掛けて、最初の数値に合計を加えます。 3番目の場合は26を2倍して加算し、4番目の場合は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;
}

CellReferencenotでもXMLにあることが保証されていることに注意してください(私がそこにいないことは初めてです)。 CellReferenceがnullの場合、セルは使用可能な左端のセルに配置されます。 RowIndexも仕様では必須ではないため、省略できます。その場合、セルは使用可能な最も高い行に配置されます。詳細については、 この質問 を参照してください。 CellReferencenullである場合、@ BCdotWEBの answer が正しいアプローチです。

19
petelids

小は美しい

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

回答を開始するには、まず this をご覧ください。

説明したように、行と列を抽出する[〜#〜] no [〜#〜]簡単な方法があります。最も近いのは、A1B2という形式のセルのCellReferenceの抽出です。これは、実際にはCOLUMN_ROW形式です。

あなたができることは、CellReferenceから行と列を抽出することです。はい、これはあなたがcharcharでチェックして数値と文字列を確認する必要があるメソッドを実装する必要があるでしょう。

A11があるとしましょう。列にインデックスを付ける必要があるときは、Aを抽出する必要があります。これはcolumn 1として与えられます。はい、それは簡単ではありませんが、セルをスキャンまたは反復するときに列を数えることを単に選択しない限り、それが唯一の方法です。

もう一度 this の質問の回答を見てください。これは同じことを行います。

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 );
3
Johan Andersson
    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

1
tejashiwini

私のシナリオでは、列名(セル番号ではなく)のみを処理する必要があり、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 Gistを参照してください。

0
Shimmy

@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;
}
0
SubodhW
    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);
            }
        }
    }
0
Joe