やりたいこと
"A"を投げると1を、"B"を投げると2を、…、"AA"を投げると27を、…、"BA"を投げると55を返す。作成時点では、Excelのシートの右端の列番号はXFDだったので、"XFD"にまで対応できていればOK。
表にまとめるとこんな感じ。
引数 | 戻り値 | $26^n$ごとに分解 |
---|---|---|
A | 1 | $1 *26^0$ |
… | … | |
Z | 26 | $26 *26^0$ |
AA | 27 | $1 *26^1+1 *26^0$ |
… | … | |
BA | 53 | $2 *26^1+1 *26^0$ |
… | … | |
ZZ | 702 | $26 *26^1+26 *26^0$ |
AAA | 703 | $1 *26^2+1 *26^1+1 *26^0$ |
… | … | |
XFD | 16384 | $24 *26^2+6 *26^1+4 *26^0$ |
どう実装すればいいか分からなくて悩むときは、このように表にしてます。すると、何かが見えてくるはずです。知らんけど。
というわけで、こんな感じで実装してみました。
列番号→列数
private static int ConvertExcelColumnToNumber(string eCol) {
var colNumber = 0;
for (var textIndex = 0; textIndex < eCol.Length; textIndex++) {
colNumber += (int)Math.Pow(26, eCol.Length - 1 - textIndex) * (eCol[textIndex] - 64);
}
return colNumber;
}
ConvertExcelColumnToNumber("XFD"); //16384
OKです。
なぜこれでいいか考えてみます。
例えば、"DB"について考えます。列番号"DB"は、"CZ"よりも右に、"EA"よりも左に存在します。言い換えると、$104$($4*26$)列目よりも右に、$131$ ($5 *26+1$)列目よりも左に存在します。このことから、列番号が2桁で、最高位がDの場合は、$105$~$130$の値をとり得ます。そして、Bには2列分の長さがあるので、$104+2$で"DB"→$106$という関係性が生まれるみたいです。
俺は、下図のような感じで$26^n$ごとに引き算をしていく考え方が一番腑に落ちました。皆さんはどうでしょうか。
ついでに、その逆も実装してみました。
列数→列番号
private static string ConvertNumberToExcelColumn(int num) {
var eColNum = new StringBuilder();
do {
eColNum.Insert(0, (char)((num % 26) + 64));
num /= 26;
} while (num > 0);
return eColNum.ToString();
}
ConvertExcelColumnToNumber(16384); //"XFD"
OKです。
なぜこれでいいか考えてみます。
例えば、$1729$について考えます。$26$を基準にして変形すると、$1729 = 2 * 26^2 + 14 * 26^1 + 13 * 26^0$となるので、先ほどの考え方を逆転させると、最高位から順番に、"B", "N", "M"と変換できるので、正解は"BNM"となることが分かります。
ちなみに、"列番号"という単語をこの記事を書く際に初めて知りました。
アルファベットやのに列"番号"なんや…って思ったんは俺だけじゃないはず。
参照
VLOOKUP関数の列番号ってなに?|数え方や指定の仕方 - 病院SEにゃんとのPCトラブル解決&Excel関数等活用術
追記(2024/4/23)
これが初記事でしたが、思ったより読まれるもんなんですね。というわけで、どう考え方でこういった実装に至ったかを追記しました。これからもうちょい気合入れて書きます。
あと、タグに26進数ってつけてたんですけど、しまった、27進数か?と思ったんですけど、厳密にいえば何進数でもないですね。そもそもExcelには0列目という考え方が無かったもので。タグ消しときました。
仮に27番目のアルファベットを@とすると、下のようになりますね。
10進数 | 26進数 | 27進数 | Excelの列番号 |
---|---|---|---|
$0$ | A | A | - |
$1$ | B | B | A |
$2$ | C | C | B |
… | … | … | … |
$10$ | K | K | J |
… | … | … | … |
$25$ | Z | Z | Y |
$26$ | BA | @ | Z |
$27$ | BB | BA | AA |
… | … | … | … |
$51$ | BZ | BX | AY |
$52$ | CA | BY | AZ |
… | … | … | … |
$54$ | CC | B@ | BB |
$55$ | CD | CA | BC |
… | … | … | … |
まあ改めて見ると一応26進数の数の増え方と若干似ていましたが、どちらとも微妙に異なる動きを取る、曲者でしたね。