PHP:数字转Excel列头 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>';

functionshowarr($arr){

echo'<pre>';

print_r($arr);

echo'</pre><br>';

}

$ea=newExcelAssistant();

$test1=$ea->GetExcelTit(27);

showarr($test1);

/**

* Excel助手

* @author RexCao 2015-01-08

*/

classExcelAssistant{

private$carr=array();//结果数组,初始值为A

private$curlet='A';//当前末尾字母,初始化为A

private$curletpi= 0;//当前字符串从右向左的位数,用来上位递归加1处理(从0开始)

private$tmpar=array('A');//临时数组,存储结果字符串上每位的字符。初始值为'A'

privatestatic$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');

privatestatic$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 列数总计

*/

publicfunctionGetExcelTit($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循环

*/

publicfunctionGetNextLetter($l){

$k= self::$b[$l];//当前字母索引

$k++;//下位字母索引

if($k== 26){

$l='A';//反转

}else{

$l= self::$a[$k];

}

return$l;

}

/**

* 递归向上位加一

* 在这里,只有一次计算结果为A后,才会向上加1,

* 但不是每个位加了之后都要往上位冒泡,所以不能遍历每个位

* @author RexCao

*/

publicfunctionRecursiveAddUp(){

//先更新最右位的字母

$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

soldier!ready for the next!

来自:http://www.cnblogs.com/windwalking/p/4232222.html

转自我的个人博客:阔野飞花 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>';

functionshowarr($arr){

echo'<pre>';

print_r($arr);

echo'</pre><br>';

}

$ea=newExcelAssistant();

$test1=$ea->GetExcelTit(27);

showarr($test1);

/**

* Excel助手

* @author RexCao 2015-01-08

*/

classExcelAssistant{

private$carr=array();//结果数组,初始值为A

private$curlet='A';//当前末尾字母,初始化为A

private$curletpi= 0;//当前字符串从右向左的位数,用来上位递归加1处理(从0开始)

private$tmpar=array('A');//临时数组,存储结果字符串上每位的字符。初始值为'A'

privatestatic$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');

privatestatic$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 列数总计

*/

publicfunctionGetExcelTit($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循环

*/

publicfunctionGetNextLetter($l){

$k= self::$b[$l];//当前字母索引

$k++;//下位字母索引

if($k== 26){

$l='A';//反转

}else{

$l= self::$a[$k];

}

return$l;

}

/**

* 递归向上位加一

* 在这里,只有一次计算结果为A后,才会向上加1,

* 但不是每个位加了之后都要往上位冒泡,所以不能遍历每个位

* @author RexCao

*/

publicfunctionRecursiveAddUp(){

//先更新最右位的字母

$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