Algoritmo para obtener el nombre de columna similar a Excel de un número

95

Estoy trabajando en un script que genera algunos documentos de Excel y necesito convertir un número en el nombre de su columna equivalente. Por ejemplo:

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

Ya escribí un algoritmo para hacerlo, pero me gustaría saber si hay formas más simples o más rápidas de hacerlo:

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

¿Conoces una mejor manera de hacerlo? ¿Quizás algo para hacerlo más simple? o una mejora del rendimiento?

Editar

La implementación de ircmaxell funciona bastante bien. Pero, voy a agregar este breve y agradable:

function num2alpha($n)
{
    for($r = ""; $n >= 0; $n = intval($n / 26) - 1)
        $r = chr($n%26 + 0x41) . $r;
    return $r;
}
Cristian
fuente
3
El primer comentario en esta página de manual puede ser útil: php.net/manual/en/function.base-convert.php#96304
Sergey Eremin
¡Guauu! Esa es una implementación breve. ¡Gracias!
Cristian
¿Ha mirado alguna de las bibliotecas existentes para generar documentos de Excel desde PHP?
Mark Baker
Sí, por supuesto. Estoy usando tu increíble biblioteca, Mark. Solo me gusta mejorar mis habilidades para escribir algoritmos ... lo bueno de esto es que después de haber terminado uno, puedes encontrar otros algoritmos que hacen exactamente lo mismo pero son más cortos.
Cristian
2
Su algoritmo corto usa 0 -> A en lugar de 1 -> A en su solicitud, ligeramente diferente a la solicitud ... si eso es lo que quiere, mire el método PHPExcel_Cell :: stringFromColumnIndex () de PHPExcel, aunque su implementación num2alpha () podría ser más rápido ... Voy a ejecutar algunas pruebas, y puedo "tomar prestado" (con permiso)
Mark Baker

Respuestas:

155

Aquí hay una función recursiva simple y agradable (basada en cero números indexados, lo que significa 0 == A, 1 == B, etc.) ...

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

Y si lo desea uno indexado (1 == A, etc.):

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

Probado con números de 0 a 10000 ...

ircmaxell
fuente
¡Gracias! ¡Me salvó 1/2 hora!
Darryl Hein
He traducido este script PHP a JS: gist.github.com/terox/161db6259e8ddb56dd77
terox
Dudo que la función recusiva sea buena o no si se usa en un bucle. ¿Podría causar un problema de espacio en la pila?
Scott Chu
91

Usando PhpSpreadsheet ( PHPExcel está en desuso )

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

Tenga en cuenta que el índice 0 da como resultado 'Z'

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


La respuesta correcta (si usa la biblioteca PHPExcel ) es:

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

y al revés:

// result = 1
$colIndex = PHPExcel_Cell::columnIndexFromString('A');
ksn135
fuente
12
stringFromColumnIndex (1) devuelve 'B'
Ulterior
1
¡Excelente! Yo uso PHPExcel. Esta es una buena respuesta.
Scott Chu
PHPExcel_Cell::stringFromColumnIndex(1)de hecho regresa 'B', edite su respuesta.
MarthyM
13

Indexado para 1 -> A, 2 -> B, etc.

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

Indexado para 0 -> A, 1 -> B, etc.

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

Aprovecha el hecho de que PHP sigue la convención de Perl cuando se trata de operaciones aritméticas en variables de caracteres y no en C. Tenga en cuenta que las variables de carácter se pueden incrementar pero no disminuir.

Mark Baker
fuente
es una buena solución, pero creo que agotará el tiempo del servidor en números grandes como 1234567789.
Tahir Raza
5

Esto servirá para la conversión (asumiendo aritmética de números enteros), pero estoy de acuerdo con los otros carteles; Solo usabase_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;
}
Lucas
fuente
Esto volverá Bpara en $number = 1lugar de Aya que 1 % 26es1
Jalal
5

Respuesta tardía, pero esto es lo que hice (para 1 == A indexado):

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);
}

Entonces, si desea convertir al revés:

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;
}
Miguel
fuente
3

Número convertido a letras de columna de Excel:

/**
 * Number convert to Excel column letters
 * 
 * 1 = A
 * 2 = B
 * 3 = C
 * 27 = AA
 * 1234567789 = CYWOQRM
 * 
 * @link https://vector.cool/php-number-convert-to-excel-column-letters-2
 * 
 * @param int  $num       欄數
 * @param bool $uppercase 大小寫
 * @return void
 */
function num_to_letters($n)
{
    $n -= 1;
    for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
        $r = chr($n % 26 + 0x41) . $r;
    return $r;
}

ex:

echo num_to_letters(1);          // A
echo num_to_letters(2);          // B
echo num_to_letters(3);          // C
echo num_to_letters(27);         // AA
echo num_to_letters(1234567789); // CYWOQRM

Las letras de las columnas de Excel se convierten en números:

/**
 * Excel column letters convert to Number
 *
 * A = 1
 * B = 2
 * C = 3
 * AA = 27
 * CYWOQRM = 1234567789
 * 
 * @link https://vector.cool/php-number-convert-to-excel-column-letters-2
 * 
 * @param string $letters
 * @return mixed
 */
function letters_to_num($a)
{
    $l = strlen($a);
    $n = 0;
    for ($i = 0; $i < $l; $i++)
        $n = $n * 26 + ord($a[$i]) - 0x40;
    return $n;
}

ex:

echo letters_to_num('A');       // 1
echo letters_to_num('B');       // 2
echo letters_to_num('C');       // 3
echo letters_to_num('AA');      // 27
echo letters_to_num('CYWOQRM'); // 1234567789
Ana
fuente
2
<?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";
?>
corsiKa
fuente
¡Buen intento! Sin embargo, fallará ... pruébalo con 2049 y verás: D
Cristian
2

Combinando la respuesta recursiva de ircmaxell, tengo esta:

    function getNameFromNumber ($ num, $ index = 0) {
        $ índice = abs ($ índice * 1); // asegúrese de que el índice sea un número entero positivo
        $ numérico = ($ num - $ índice)% 26; 
        $ letra = chr (65 + $ numérico);
        $ num2 = intval (($ num - $ índice) / 26);
        si ($ num2> 0) {
            return getNameFromNumber ($ num2 - 1 + $ índice). $ letra;
        } más {
            return $ letra;
        }
    }

Estoy usando la indexación predeterminada como basada en 0, pero puede ser cualquier número entero positivo cuando hago malabares con matrices en PHP.

Charlie Affumigato
fuente
2

Nunca usaría esto en producción porque no es legible, pero por diversión ... Solo lo hace hasta ZZ.

<?php
    $col = 55;
    print (($n = (int)(($col - 1) / 26)) ? chr($n + 64) : '') . chr((($col - 1) % 26) + 65);
?>
simiesy
fuente
0

Para cualquiera que busque una implementación de Javascript de esto, aquí está la respuesta de @ ircmaxell en Javascript .

function getNameFromNumber(num){
    let numeric = num%26;
    let letter = String.fromCharCode(65+numeric);
    let num2 = parseInt(num/26);
    if(num2 > 0) {
      return getNameFromNumber(num2 - 1)+letter;
    } else {
      return letter;
    }
}
Cels
fuente