代码之家  ›  专栏  ›  技术社区  ›  Cristian

获取数字类似Excel的列名的算法

  •  77
  • Cristian  · 技术社区  · 15 年前

    我正在编写一个生成一些Excel文档的脚本,我需要将一个数字转换为等效的列名。例如:

    1 => A
    2 => B
    27 => AA
    28 => AB
    14558 => UMX
    

    我已经写了一个算法来实现这一点,但我想知道是更简单还是更快的方法:

    function numberToColumnName($number){
        $abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        $abc_len = strlen($abc);
    
        $result_len = 1; // how much characters the column's name will have
        $pow = 0;
        while( ( $pow += pow($abc_len, $result_len) ) < $number ){
            $result_len++;
        }
    
        $result = "";
        $next = false;
        // add each character to the result...
        for($i = 1; $i<=$result_len; $i++){
            $index = ($number % $abc_len) - 1; // calculate the module
    
            // sometimes the index should be decreased by 1
            if( $next || $next = false ){
                $index--;
            }
    
            // this is the point that will be calculated in the next iteration
            $number = floor($number / strlen($abc));
    
            // if the index is negative, convert it to positive
            if( $next = ($index < 0) ) {
                $index = $abc_len + $index;
            }
    
            $result = $abc[$index].$result; // concatenate the letter
        }
        return $result;
    }
    

    你知道更好的方法吗?也许是一些简单的东西?还是绩效提升?

    编辑

    ircmaxell的实现工作得很好。但是,我要加上这个漂亮的短的:

    function num2alpha($n)
    {
        for($r = ""; $n >= 0; $n = intval($n / 26) - 1)
            $r = chr($n%26 + 0x41) . $r;
        return $r;
    }
    
    8 回复  |  直到 7 年前
        1
  •  131
  •   ircmaxell    15 年前

    这是一个很好的简单递归函数(基于零索引数,意味着0==a,1==b等)。

    function getNameFromNumber($num) {
        $numeric = $num % 26;
        $letter = chr(65 + $numeric);
        $num2 = intval($num / 26);
        if ($num2 > 0) {
            return getNameFromNumber($num2 - 1) . $letter;
        } else {
            return $letter;
        }
    }
    

    如果你想要它一个索引(1==A,等等):

    function getNameFromNumber($num) {
        $numeric = ($num - 1) % 26;
        $letter = chr(65 + $numeric);
        $num2 = intval(($num - 1) / 26);
        if ($num2 > 0) {
            return getNameFromNumber($num2) . $letter;
        } else {
            return $letter;
        }
    }
    

    用0到10000的数字进行测试…

        2
  •  75
  •   ksn135    7 年前

    使用 PHP电子表格 ( phpexcel已弃用 )

    // result = 'A'
    \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex(1);
    

    “z”中的注释索引0结果

    https://phpspreadsheet.readthedocs.io/en/develop/


    正确答案(如果使用 PHPExcel 图书馆是:

    // result = 'A'
    $columnLetter = PHPExcel_Cell::stringFromColumnIndex(0); // ZERO-based! 
    

    向后:

    // result = 1
    $colIndex = PHPExcel_Cell::columnIndexFromString('A');
    
        3
  •  12
  •   Mark Baker    15 年前

    为1->a、2->b等编制索引

    function numToExcelAlpha($n) {
        $r = 'A';
        while ($n-- > 1) {
            $r++;
        }
        return $r;
    }
    

    为0->a、1->b等编制索引

    function numToExcelAlpha($n) {
        $r = 'A';
        while ($n-- >= 1) {
            $r++;
        }
        return $r;
    }
    

    利用这样一个事实:当处理字符变量而不是C变量的算术运算时,PHP遵循Perl的约定。请注意,字符变量可以递增,但不能递减。

        4
  •  4
  •   Lucas    15 年前

    这对转换(假设整数算术)是有用的,但我同意其他海报;只需使用 base_convert

    function numberToColumnName($number)
    {
        $abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        $len = strlen($abc);
    
        $result = "";
        while ($number > 0) {
           $index  = $number % $len;
           $result = $abc[$index] . $result;
           $number = floor($number / $len);
        }
    
        return $result;
    }
    
        5
  •  2
  •   Mike    11 年前

    回答迟了,但我做了以下工作(1=索引):

    function num_to_letters($num, $uppercase = true) {
        $letters = '';
        while ($num > 0) {
            $code = ($num % 26 == 0) ? 26 : $num % 26;
            $letters .= chr($code + 64);
            $num = ($num - $code) / 26;
        }
        return ($uppercase) ? strtoupper(strrev($letters)) : strrev($letters);
    }
    

    如果你想用另一种方式转换:

    function letters_to_num($letters) {
        $num = 0;
        $arr = array_reverse(str_split($letters));
    
        for ($i = 0; $i < count($arr); $i++) {
            $num += (ord(strtolower($arr[$i])) - 96) * (pow(26,$i));
        }
        return $num;
    }
    
        6
  •  1
  •   corsiKa    15 年前
    <?php
    function numberToColumnName($number){
        $abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        $abc_len = strlen($abc);
    
        $result = "";
        $tmp = $number;
    
        while($number > $abc_len) {
            $remainder = $number % $abc_len;
            $result = $abc[$remainder-1].$result;
            $number = floor($number / $abc_len);
        }
        return $abc[$number-1].$result;
    }
    
    echo numberToColumnName(1)."\n";
    echo numberToColumnName(25)."\n";
    echo numberToColumnName(26)."\n";
    echo numberToColumnName(27)."\n";
    echo numberToColumnName(28)."\n";
    echo numberToColumnName(14558)."\n";
    ?>
    
        7
  •  1
  •   Charlie Affumigato    13 年前

    结合ircmaxell的递归答案,我得到了一个:

        function getNameFromNumber($num, $index=0) {
            $index = abs($index*1); //make sure index is a positive integer
            $numeric = ($num - $index) % 26; 
            $letter = chr(65 + $numeric);
            $num2 = intval(($num -$index) / 26);
            if ($num2 > 0) {
                return getNameFromNumber($num2 - 1 + $index) . $letter;
            } else {
                return $letter;
            }
        }
    
    

    我使用的是基于0的默认索引,但是当在PHP中处理数组时,它可以是任何正整数。

        8
  •  0
  •   simesy    8 年前

    我从不在制作中使用这个,因为它不可读,但为了好玩… 只有到ZZ。

    <?php
        $col = 55;
        print (($n = (int)(($col - 1) / 26)) ? chr($n + 64) : '') . chr((($col - 1) % 26) + 65);
    ?>
    
    推荐文章