An Embedded Engineer’s Blog

とある組み込みエンジニアの備忘録的なブログです。

Excelの列名 <—> 列番号相互変換

まえがき

Excelの列名(A, B, ..., AA, AB)と列番号(1, 2, ...)を相互変換する方法のメモです。
ついでに、相互変換するWindows用アプリケーションも作成しました。

列名 <--> 列番号相互変換

共通定数

まずは、共通で使用する定数を定義します。

// アルファベットの文字数(26文字)
public const ulong AlphabetNum = ('Z' - 'A' + 1ul);

// 列名判定用正規表現パターン
public const string LabelPattern = @"^[A-Z]+$";

// 列番号判定用正規表現パターン
public const string IndexPattern = @"^[1-9][0-9]*$";


列名 --> 列番号

列名から列番号への変換は、アルファベット(A - Z)を1から26までの数値に置き換え、26進数のような形で計算することで算出できます。


 index = X_{n} * 26^{(n-1)} + X_{(n-1)}  * 26^{(n-2)}  + ... + X_{1} * 26^{0}
 n : \mbox{列名の桁数}
 X : \mbox{アルファベットを数値に変換した値}


Ex)

列名 計算式 結果
A  1 * 26^ 0 1
B  2 * 26^ 0 2
Z  26 * 26^ 0 26
AA  (1 * 26^ 1) + (1 * 26^ 0) 27
AB  (1 * 26^ 1) + (2 * 26^ 0) 28
ZZ  (26 * 26^ 1) + (26 * 26^ 0) 702


public static string ConvertLabelToIndex(string label)
{
    // 入力文字列が列名パターンにマッチしない
    if (!Regex.IsMatch(label, LabelPattern))
    {
        // 入力値エラー
        throw new ArgumentException($"無効なラベル名です : {label}");
    }
    else
    {
        // 出力用列番号初期化
        var index = 0ul;

        // 底の初期化(n = 26^x : x = 0)
        var n = 1ul;

        // 入力列名を文字配列に変換(ex : ABC -> A, B, C)
        var array = label.ToList();

        // 文字配列を反転(ex : A, B, C -> C, B, A)
        array.Reverse();

        // 反転した文字配列を1文字ずつ走査
        foreach (var c in array)
        {
            // 現在のアルファベット文字を数値に変換(A - Z : 1 - 26)
            var x = (ulong)(c - 'A') + 1ul;

            // アルファベットに対応する数値と底を乗算
            var y = x * n;

            // 乗算した結果を列番号に加算
            index += y;

            // 次の桁の底を算出(26^0 -> 26^1 -> 26^2)
            n *= ColumnIndexConverter.AlphabetNum;
        }

        // 列番号を文字列に変換して出力
        return $"{index}";
    }
}


列番号 --> 列名

列番号から列名への変換は、列名から列番号への変換と逆のことをやれば良いということになります。

  1. 列番号を入力値にセット( x = index)
  2. 入力値が0始まりになるように1減算( x = x - 1)
  3. 入力値とアルファベット文字数(26)の剰余を算出( m = Mod(x, 26))
  4. 算出した剰余(0 - 25)をアルファベット文字(A - Z)に変換( A = \mbox{'A'} + m)
  5. 変換したアルファベット文字を列名の先頭に追加([text: label = A + label])
  6. 入力値とアルファベット文字数の商を次の入力値にセット( x = x / 26)
  7. 入力値が0になるまで2〜5を繰り返す


Ex1) 列番号 = 1

  1.  x = 1
  2.  x = x - 1 = 0
  3.  m = Mod(x, 26) = Mod(0, 26) = 0
  4.  A = \mbox{'A'} + m = \mbox{'A'} + 0 = \mbox{'A'}
  5.  label = \mbox{'A'} + label = \mbox{'A'} + \mbox{""} = \mbox{"A"}
  6.  x = x / 26 = 0 / 26 = 0
  7.  x = 0のため終了


Ex2) 列番号 = 2

  1.  x = 2
  2.  x = x - 1 = 1
  3.  m = Mod(x, 26) = Mod(1, 26) = 1
  4.  A = \mbox{'A'} + m = \mbox{'A'} + 1 = \mbox{'B'}
  5.  label = \mbox{'B'} + label = \mbox{'B'} + \mbox{""} = \mbox{"B"}
  6.  x = x / 26 = 0 / 26 = 0
  7.  x = 0のため終了


Ex3) 列番号 = 28

  1.  x = 28
  2.  x = x - 1 = 27
  3.  m = Mod(x, 26) = Mod(27, 26) = 1
  4.  A = \mbox{'A'} + m = \mbox{'A'} + 1 = \mbox{'B'}
  5.  label = \mbox{'B'} + label = \mbox{'B'} + \mbox{""} = \mbox{"B"}
  6.  x = x / 26 = 27 / 26 = 1
  7.  x = x - 1 = 0
  8.  m = Mod(x, 26) = Mod(0, 26) = 0
  9.  A = \mbox{'A'} + m = \mbox{'A'} + 0 = \mbox{'A'}
  10.  label = \mbox{'A'} + label = \mbox{'A'} + \mbox{"B"} = \mbox{"AB"}
  11.  x = x / 26 = 0 / 26 = 0
  12.  x = 0のため終了


public static string ConvertIndexToLabel(string index)
{
    // 入力文字列が列番号パターンにマッチしない
    if (!Regex.IsMatch(index, IndexPattern))
    {
        // 入力値エラー
        throw new ArgumentException($"無効なインデックスです : {index}");
    }
    else
    {
        // 入力文字列を数値に変換
        var value = ulong.Parse(index);

        // 入力数値が0以下
        if (value <= 0)
        {
            // 入力値エラー
            throw new ArgumentException($"無効なインデックスです : {index}");
        }
        else
        {
            // 出力用列名を初期化
            var label = string.Empty;

            // アルファベット文字数(26)を取得
            var a = ColumnIndexConverter.AlphabetNum;

            // 入力値が0より大きい間繰り返す
            while (value > 0)
            {
                // 入力値を0始まりになるようにデクリメント
                value--;

                // 入力値の剰余を文字に変換
                var c = (char)('A' + (value % a));

                // 変換した文字を列名の先頭に追加
                label = $"{c}{label}";

                // 入力値とアルファベット文字数の商を次の入力値にセット
                value /= a;
            }

            // 列名を出力
            return label;
        }
    }
}


相互変換アプリケーション

CUI版と、GUI版を作成しました。
GitHubにアップしていますので、詳細はそちらをご覧ください。

f:id:an-embedded-engineer:20190504174446p:plain:w500
CUIアプリケーション


f:id:an-embedded-engineer:20190504174513p:plain:w500
GUIアプリケーション(列番号 → 列名)


f:id:an-embedded-engineer:20190504174615p:plain:w500
GUIアプリケーション(列名 → 列番号)