PHP:数字转Excel列头
转自我的个人博客:阔野飞花 http://www.rexcao.net/archives/169
前段时间升级一个项目的Excel导出功能,这次的列数大概有60多条,在处理过程中发现一个问题,原先做好的数字转Excel列头功能现在只到 AZ列就结束了,那显然是不够用啊,后来再仔细查看,发现,原来AZ列之后的内容显示到AAA列上面了,然后看了看原来的代码才发现,原来的逻辑错了!
我原来的错误逻辑是这样的:A-Z,Z下来是AA,AA-AZ,AZ下来是AAA,下来是AAAA依次类推...但是Excel中AZ下来是BZ!意识到这一点之后,就着手开始修改自己原先的代码。
阶段一
数字转Excel列头,目标是提供任意一个数字,将其通过一个固定的方法转为Excel的列头,在此,我准备通过26位数作为临界值取整,取余来判断,后来发现有些绕,就干脆当作26进制来处理了。
阶段二
当 作26进制来处理之后,折腾了一会又发现一个新的问题,对于Excel列头来说,它的计算单位是A-Z,用十进制来类比,十进制数字的计算单位是 0-9,Excel列头Z的下一位是AA,而十进制9的下一位是10,如果A代表0的话,Excel列头Z就相当于是最大的十进制单位9,但是十进制的 10的上位是1,它代表尚未是有值的,而看看Excel列头AA的上位,是A,A代表0,那意思是上位没有值?显然不合理,所以我发现,这里也不能纯粹地 当作26进制来处理。
阶段三
经过上面两个阶段的折腾后,我决定采用另外一种办法,大体思路如下:
1、不再固定计算每个数字对应的列头字母,而是计算指定数字个列头(例如2个就是A、B,3个就是A、B、C等等)。
2、每次加1后,当前字母也加1(相当于1+1=2,A+1=B)。
3、计算结果是多个字母的拼接结果(A拼接A=AA,A拼接B=AB)。
4、低位加1的结果大于Z的话,向上位进一,进一之后的这个上位如果还大于Z的话,继续向上进一...(向上冒泡)
结果
既然思路已经清晰了,那么可以看看实现了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
header(
'Content-Type:text/html; charset=utf-8'
);
echo
'Convert number to title of excel.<br>'
;
function
showarr(
$arr
){
echo
'<pre>'
;
print_r(
$arr
);
echo
'</pre><br>'
;
}
$ea
=
new
ExcelAssistant();
$test1
=
$ea
->GetExcelTit(27);
showarr(
$test1
);
/**
* Excel助手
* @author RexCao 2015-01-08
*/
class
ExcelAssistant{
private
$carr
=
array
();
//结果数组,初始值为A
private
$curlet
=
'A'
;
//当前末尾字母,初始化为A
private
$curletpi
= 0;
//当前字符串从右向左的位数,用来上位递归加1处理(从0开始)
private
$tmpar
=
array
(
'A'
);
//临时数组,存储结果字符串上每位的字符。初始值为'A'
private
static
$a
=
array
(
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
,
'G'
,
'H'
,
'I'
,
'J'
,
'K'
,
'L'
,
'M'
,
'N'
,
'O'
,
'P'
,
'Q'
,
'R'
,
'S'
,
'T'
,
'U'
,
'V'
,
'W'
,
'X'
,
'Y'
,
'Z'
);
private
static
$b
=
array
(
'A'
=>0,
'B'
=>1,
'C'
=>2,
'D'
=>3,
'E'
=>4,
'F'
=>5,
'G'
=>6,
'H'
=>7,
'I'
=>8,
'J'
=>9,
'K'
=>10,
'L'
=>11,
'M'
=>12,
'N'
=>13,
'O'
=>14,
'P'
=>15,
'Q'
=>16,
'R'
=>17,
'S'
=>18,
'T'
=>19,
'U'
=>20,
'V'
=>21,
'W'
=>22,
'X'
=>23,
'Y'
=>24,
'Z'
=>25);
/**
* 获取excel列头
* @param $num 列数总计
*/
public
function
GetExcelTit(
$num
){
$i
= 0;
while
(
$i
<
$num
){
$this
->curletpi = 0;
//没有处理上位的时候,只处理当前位
$this
->tmpar[
count
(
$this
->tmpar)-1] =
$this
->curlet;
$this
->carr[] = implode(
''
,
$this
->tmpar);
$this
->curlet =
$this
->GetNextLetter(
$this
->curlet);
if
(
$this
->curlet ==
'A'
){
//说明过了一圈,该向上位递归加1了
$this
->curletpi++;
//从当前位左边的那位开始处理
$this
->RecursiveAddUp();
}
$i
++;
}
return
$this
->carr;
}
/**
* 根据字母获取下位字母
* A-Z循环
*/
public
function
GetNextLetter(
$l
){
$k
= self::
$b
[
$l
];
//当前字母索引
$k
++;
//下位字母索引
if
(
$k
== 26){
$l
=
'A'
;
//反转
}
else
{
$l
= self::
$a
[
$k
];
}
return
$l
;
}
/**
* 递归向上位加一
* 在这里,只有一次计算结果为A后,才会向上加1,
* 但不是每个位加了之后都要往上位冒泡,所以不能遍历每个位
* @author RexCao
*/
public
function
RecursiveAddUp(){
//先更新最右位的字母
$this
->tmpar[
count
(
$this
->tmpar)-1] =
'A'
;
if
(
$this
->curletpi+1>
count
(
$this
->tmpar)){
$this
->tmpar =
array_merge
(
array
(
'A'
),
$this
->tmpar);
}
else
{
$cl
=
$this
->tmpar[
count
(
$this
->tmpar)-
$this
->curletpi-1];
//当前位的字母
$cl
=
$this
->GetNextLetter(
$cl
);
if
(
$cl
==
'A'
){
$this
->tmpar[
count
(
$this
->tmpar)-
$this
->curletpi-1] =
'A'
;
//要去处理更上位了,先更新本位
$this
->curletpi++;
//再上一位
$this
->RecursiveAddUp();
}
else
{
//更新当前位的字母为新字母即可
$this
->tmpar[
count
(
$this
->tmpar)-
$this
->curletpi-1] =
$cl
;
}
}
}
}
|
有兴趣看运行结果的,可以去我的个人博客瞧瞧咯(●'◡'●)。阔野飞花 http://www.rexcao.net/archives/169