
生成类似excel横坐标的字母序列
前言
做业务有时候需要导出excel,还得动态设置样式,代码循环的时候只知道当前列的数字位置,不知道excel中表示的字母编号,需要动态的算出表格横坐标的字母编号,下面给出可以直接拿去使用的代码以及生成过程的说明。
序列号(数字)生成字母序列
类似excel表格
我们的需求:传入一个序列号(数字),表示横坐标的数字形式,从0开始,例如 0 则表示 A,1 则表示 B,25 表示 Z,26 表示 AA,... 以此类推,702 表示 AAA,730 表示 ABC
我们先给字母 A 到 Z 定个编号,从 0 到 25
寻找规律
0-25 表示 A-Z 字母序列 = ([字母编号])
26-701 表示 AA-ZZ 字母序列 = ( [floor(序列号/26)-1 % 26] | [序列号%26] )
702-18277 表示 AAA-ZZZ 字母序列 = ( [floor((floor(序列号/26)-1)/26)-1 % 26] | [向下取整(序列号/26)-1 % 26] | [序列号%26] )
输入 730 ,得到 ABC
我们给输出的字符串从右到左的每个字符位置编号
可以看出,当输出只有一个字母的时候,序列号=字母编号
当输出多个字母的时候,
0 号位置的 字母编号 = 730 % 26,
1 号位置的 字母编号 = floor(730 / 26) - 1 % 26,
2 号位置的 字母编号 = floor(floor(730 / 26) - 1) / 26 - 1 % 26
从 0 号位置到 2 号位置,每次多除了一次26
直观一点的计算过程如下
0号位:730 / 26 = 28 余 2 --- C
1号位:(28 - 1) / 26 = 1 余 1 --- B
2号位:(1 - 1) /26 = 0 余 0 --- A
所以字符串的每个字母就是余数对应的字符
java代码如下:
/**
* 生成字母序列号,类似excel表格的列名(A、B、C、...AA、AB、AC...)
*
* @param serialNum 序列号,从0开始;0 -> a,25 -> z,26 -> aa,702 -> aaa, 730 -> abc
* @param upper 是否输出大写
* @return 结果
*/
public static String genLetterSerial(int serialNum, boolean upper) {
if (serialNum < 0) {
return "";
}
StringBuilder sb = new StringBuilder();
// Ascii,A:65,a:97
int letterA = upper ? 65 : 97;
int chunkCount = (int) Math.floor(serialNum / 26.0);
int pos = serialNum % 26;
do {
char c = (char) (letterA + pos);
sb.append(c);
if (chunkCount < 1) {
break;
}
int chunkNum = chunkCount - 1;
pos = chunkNum % 26;
chunkCount = (int) Math.floor(chunkNum / 26.0);
} while (true);
return sb.reverse().toString();
}
逆向(字母序列反向算出序列号,如 ABC -> 730)
从字母序列逆向算出序列号的过程也很简单
还是用 730 -> ABC 的例子
0号位:730 / 26 = 28 余 2
1号位:(28 - 1) / 26 = 1 余 1
2号位:(1 - 1) /26 = 0 余 0
定义一条公式 (x - 1) / 26 = z 余 y
可以看得出来,我们只需要知道 0 号位置的计算结果以及余数就可以得到最终的 序列号 = 28 * 26 + 2 = 730
把 (x - 1) / 26 = z 余 y 套到上面的过程中,可以发现,其实 2 号位置的 x 是 1 号位置的 z ,1 号位置的 x 是 0 号位置的 z
x=z*26 + y + 1
这不就简单了吗,我们只要从2号位置开始一步一步算出 0 号位置的 z 和 y,就能得到最终的序列号
逆向开始时,我们只能确定每个位置的余数是什么(根据每个位置的字母可以确定),以及最后一位(2号位)的被除数一定是(1 - 1),商一定是 0
我们从 2 号位开始算
2号位:y=0, z=0, x=1
1号位:y=1, z=2号位x = 1, x=1*26 + 1 + 1 = 28
0号位:y=2, z=1号位x = 28, x=28*26 + 1 + 1 = 732
最终序列号:z * 26 + y = 28 * 26 + 2 = 730
java代码如下:
/**
* genLetterSerial的逆过程,根据字母序列,逆向出序列号
*
* @param letterSerial 字母序列字符串,如 a -> 0, abc -> 730
* @return 结果
*/
public static int letterSerialToSerialNum(String letterSerial) {
if (Objects.equals(letterSerial, "")) {
return -1;
}
// 全部字母转小写
letterSerial = letterSerial.toLowerCase();
char[] charArray = letterSerial.toCharArray();
int z = 0;
int y = charArray[0] - 97;
for (int i = 1; i < charArray.length; i++) {
z = z * 26 + y + 1;
y = charArray[i] - 97;
}
return z * 26 + y;
}