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>' ;
 
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

soldier!ready for the next!
 
来自:http://www.cnblogs.com/windwalking/p/4232222.html

转载于:https://www.cnblogs.com/i6010/articles/6282040.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值