Algoritmo per ottenere il nome di colonna di tipo Excel di un numero


95

Sto lavorando a uno script che genera alcuni documenti Excel e devo convertire un numero nell'equivalente del nome della sua colonna. Per esempio:

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

Ho già scritto un algoritmo per farlo, ma mi piacerebbe sapere se sono modi più semplici o più veloci per farlo:

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

Conosci un modo migliore per farlo? Forse qualcosa per renderlo più semplice? o un miglioramento delle prestazioni?

modificare

L'implementazione di ircmaxell funziona abbastanza bene. Ma aggiungerò questo simpatico breve:

function num2alpha($n)
{
    for($r = ""; $n >= 0; $n = intval($n / 26) - 1)
        $r = chr($n%26 + 0x41) . $r;
    return $r;
}

3
il primo commento in questa pagina man potrebbe essere utile: php.net/manual/en/function.base-convert.php#96304
Sergey Eremin

Wow! Questa è una breve implementazione. Grazie!
Cristian

Hai esaminato una delle librerie esistenti per generare documenti Excel da PHP?
Mark Baker

Sì, naturalmente. Sto usando la tua fantastica libreria, Mark. Mi piace solo migliorare le mie capacità nello scrivere algoritmi ... la cosa bella è che dopo averne finito uno, puoi trovare altri algoritmi che fanno esattamente lo stesso ma sono più brevi.
Cristian

2
Il tuo algoritmo breve usa 0 -> A invece di 1 -> A nella tua richiesta, leggermente diverso dalla richiesta ... se è quello che vuoi, guarda il metodo PHPExcel_Cell :: stringFromColumnIndex () di PHPExcel, sebbene la tua implementazione num2alpha () potrebbe essere più veloce ... eseguirò alcuni test e potrei "prenderlo in prestito" (con il permesso)
Mark Baker

Risposte:


155

Ecco una bella semplice funzione ricorsiva (basata su zero numeri indicizzati, che significa 0 == A, 1 == B, ecc.) ...

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

E se ne vuoi uno indicizzato (1 == A, ecc.):

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

Testato con numeri da 0 a 10000 ...


Grazie! Mi ha risparmiato mezz'ora!
Darryl Hein

Ho tradotto questo script PHP in JS: gist.github.com/terox/161db6259e8ddb56dd77
terox

Dubito che la funzione recusiva sia buona o meno se usata in un ciclo? Potrebbe causare problemi di spazio nello stack?
Scott Chu

92

Utilizzo di PhpSpreadsheet ( PHPExcel è deprecato )

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

L'indice della nota 0 restituisce "Z"

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


La risposta corretta (se usi PHPExcel Library) è:

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

e indietro:

// result = 1
$colIndex = PHPExcel_Cell::columnIndexFromString('A');

12
stringFromColumnIndex (1) restituisce 'B'
Ulterior

1
Eccellente! Mi capita di usare PHPExcel. Questa è una risposta chiara.
Scott Chu

PHPExcel_Cell::stringFromColumnIndex(1)effettivamente restituisce 'B', si prega di modificare la risposta.
MarthyM

13

Indicizzato per 1 -> A, 2 -> B, ecc

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

Indicizzato per 0 -> A, 1 -> B, ecc

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

Sfrutta il fatto che PHP segue la convenzione di Perl quando si tratta di operazioni aritmetiche su variabili carattere e non di C. Notare che le variabili carattere possono essere incrementate ma non decrementate.


è una buona soluzione ma penso che metterà in timeout il server su numeri grandi come 1234567789.
Tahir Raza

5

Questo andrà bene per la conversione (assumendo l'aritmetica dei numeri interi), ma sono d'accordo con gli altri poster; basta usarebase_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;
}

Ciò restituirà Bper $number = 1anziché Adal 1 % 26IS1
Jalal

5

Risposta tardiva, ma ecco cosa ho fatto (per 1 == A indicizzato):

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

Quindi, se vuoi convertire nell'altro modo:

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

3

Numero convertito in lettere di colonna 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

Le lettere delle colonne di Excel vengono convertite in numero:

/**
 * 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

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";
?>

Bel tentativo! Ma fallirà ... provaci con 2049 e vedrai: D
Cristian

2

Combinando la risposta ricorsiva di ircmaxell ho ottenuto questa:

    funzione getNameFromNumber ($ num, $ index = 0) {
        $ indice = abs ($ indice * 1); // assicurati che index sia un numero intero positivo
        $ numerico = ($ num - $ indice)% 26; 
        $ lettera = chr (65 + $ numerico);
        $ num2 = intval (($ num - $ indice) / 26);
        if ($ num2> 0) {
            restituisci getNameFromNumber ($ num2 - 1 + $ index). $ letter;
        } altro {
            return $ letter;
        }
    }

Sto usando l'indicizzazione predefinita come 0 basata, ma può essere qualsiasi numero intero positivo per quando si destreggia con gli array in PHP.


2

Non lo userei mai in produzione perché non è leggibile, ma per divertimento ... Funziona solo fino a ZZ.

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

0

Per chiunque cerchi un'implementazione Javascript di questo, ecco la risposta di @ ircmaxell in 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;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.