转自我的个人博客:阔野飞花 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的话,继续向上进一...(向上冒泡)

结果

既然思路已经清晰了,那么可以看看实现了。

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

PHP:数字转Excel列头的更多相关文章

  1. JAVA获取EXCEL列头

    FileInputStream fileInputStream = new FileInputStream(rootPath + path + "/" + fileName); L ...

  2. 转载: 黄聪:C#中 Excel列字母与数字的转换

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  3. C#导入Excel遇到数字字母混合列数据丢失解决

    错误重现: ----------------------------------------------------------------------- 在导入Excel读取数据时,其中的一个字段保 ...

  4. 把EXCEL列号数字变成字母

    把Excel 列号数字变成字母 private static string ToName(int index) { if (index < 0) { throw new Exception(&q ...

  5. [No0000107]C#中 Excel列字母与数字的转换

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. NPOI操作Excel时使用列头来读取数据的方法

    首先定义扩展方法: public static ICell GetCell(this IRow row, string clounmName) { IRow firstRow = row.Sheet. ...

  7. C# listview 单击列头实现排序 <二>

    单击列头实现排序,首先在羡慕中添加下面的帮助实现的类:具体的代码: using System; using System.Collections; using System.Windows.Forms ...

  8. datatable的部分问题处理(动态定义列头,给某行添加事件,初始显示空数据)

    一.动态定义列头 在ajax中,用datatable再去重新配置列头,当然传回的数据中,要有对应放列头的键值对 我自定义了Mock数据,用于前端自己交互. 其中,rowdata用于存放传回的数据,co ...

  9. 【转】silverlight telerik RadGridView 列头显示其他控件

    <telerik:GridViewDataColumn DataMemberBinding="{Binding target_id}" IsFilterable=" ...

随机推荐

  1. 包括ES6在内的数组操作(待更)

    下面是我对ES6和古老的JS(ES3)一些数组操作的总结,附带了一些我曾经用上的. map处有待更内容. 贴一下有借鉴的网站:https://segmentfault.com/a/1190000002 ...

  2. 【算法笔记】B1021 个位数统计

    1021 个位数统计 (15 分) 给定一个 k 位整数 N=d​k−1​​10​k−1​​+⋯+d​1​​10​1​​+d​0​​ (0≤d​i​​≤9, i=0,⋯,k−1, d​k−1​​> ...

  3. BZOJ 5168 && Luogu P3740 [HAOI2014]贴海报 线段树~~

    据说某谷数据十分水...但幸好BZOJ上也过了...话说我记得讲课时讲的是奇奇怪怪的离散化..但现在突然觉得什么都可以线段树瞎搞了...QAQ 直接就是这个区间有没有被覆盖,被覆盖直接return: ...

  4. UESTC - 1610 递推方程+矩阵快速幂

    感觉像是HDU Keyboard的加强版,先推出3张牌时的所有组合,然后递推出n张牌 看到n=1e18时吓尿了 最后24那里还是推错了.. (5行1列 dp[1][n],dp[2][n],dp[3][ ...

  5. Laravel项目部署到Nginx服务器除了/目录,全飘404

    https://blog.csdn.net/yoywow/article/details/52153610 不管是Nginx还是Apache,如果不配置,都会出现404,不能路由. 我云服务器安装的是 ...

  6. PHP中Session ID的实现原理

    前序: 首先要明白PHPSESSID看似多次刷新都不会改变其实是没有删除本地相关联的cookie,删除的方法 session_destroy();//删除服务器端的session文件 setcooki ...

  7. sqlmap命令小结

    --technique 这个参数可以指定sqlmap使用的探测技术,默认情况下会测试所有的方式. 支持的探测方式如下: B: Boolean-based blind SQL injection(布尔型 ...

  8. TensorFlow-多层感知机(MLP)

    TensorFlow训练神经网络的4个步骤: 1.定义算法公式,即训练神经网络的forward时的计算 2.定义损失函数和选择优化器来优化loss 3.训练步骤 4.对模型进行准确率评测 附Multi ...

  9. 理解Call、Apply、bind

    Apply.call 共同点: 为了改变函数执行时的上下文(简单说就是为了改变当前函数体内的This的指向) 不同点: 传入的参数不一样,func.apply(this,[arg1,arg2]).fu ...

  10. 转 How To Stop A Running Job Using DBMS_JOB

    There is no procedure within the dbms_job package to stop a running job.You will need to determine w ...