Algorithme pour obtenir le nom de colonne de type Excel d'un nombre

95

Je travaille sur un script qui génère des documents Excel et je dois convertir un nombre en son équivalent de nom de colonne. Par exemple:

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

J'ai déjà écrit un algorithme pour le faire, mais j'aimerais savoir s'il existe des moyens plus simples ou plus rapides de le faire:

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

Connaissez-vous une meilleure façon de le faire? Peut-être quelque chose pour simplifier les choses? ou une amélioration des performances?

Éditer

L'implémentation d'ircmaxell fonctionne assez bien. Mais, je vais ajouter ce joli court:

function num2alpha($n)
{
    for($r = ""; $n >= 0; $n = intval($n / 26) - 1)
        $r = chr($n%26 + 0x41) . $r;
    return $r;
}
Cristian
la source
3
Le premier commentaire de cette page de manuel pourrait être utile: php.net/manual/en/function.base-convert.php#96304
Sergey Eremin
Hou la la! C'est une courte mise en œuvre. Je vous remercie!
Cristian
Avez-vous regardé l'une des bibliothèques existantes pour générer des documents Excel à partir de PHP?
Mark Baker
Oui bien sûr. J'utilise votre superbe bibliothèque, Mark. J'aime simplement améliorer mes compétences en écriture d'algorithmes ... ce qui est bien, c'est qu'après en avoir terminé un, vous pouvez trouver d'autres algorithmes qui font exactement la même chose mais qui sont plus courts.
Cristian
2
Votre algorithme court utilise 0 -> A plutôt que 1 -> A dans votre requête, légèrement différent de la requête ... si c'est ce que vous voulez, regardez la méthode PHPExcel_Cell :: stringFromColumnIndex () de PHPExcel, bien que votre implémentation num2alpha () puisse être plus rapide ... Je vais faire des tests, et je peux "l'emprunter" (avec permission)
Mark Baker

Réponses:

155

Voici une jolie fonction récursive simple (basée sur zéro nombre indexé, ce qui signifie 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;
    }
}

Et si vous en voulez un indexé (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;
    }
}

Testé avec des nombres de 0 à 10000 ...

ircmaxell
la source
Merci! M'a sauvé 1/2 heure!
Darryl Hein
J'ai traduit ce script PHP en JS: gist.github.com/terox/161db6259e8ddb56dd77
terox
Je doute que la fonction récusive soit bonne ou non si elle est utilisée dans une boucle? Cela pourrait-il causer un problème d'espace dans la pile?
Scott Chu
92

Utilisation de PhpSpreadsheet ( PHPExcel est obsolète )

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

L'index de note 0 donne «Z»

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


La bonne réponse (si vous utilisez la bibliothèque PHPExcel ) est:

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

et à l'envers:

// result = 1
$colIndex = PHPExcel_Cell::columnIndexFromString('A');
ksn135
la source
12
stringFromColumnIndex (1) renvoie 'B'
Ulterior
1
Excellent! J'utilise PHPExcel. C'est une bonne réponse.
Scott Chu
PHPExcel_Cell::stringFromColumnIndex(1)retourne effectivement 'B', veuillez modifier votre réponse.
MarthyM le
13

Indexé pour 1 -> A, 2 -> B, etc.

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

Indexé pour 0 -> A, 1 -> B, etc.

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

Profite du fait que PHP suit la convention de Perl lorsqu'il traite des opérations arithmétiques sur des variables de caractères et non sur des C. Notez que les variables de caractères peuvent être incrémentées mais pas décrémentées.

Mark Baker
la source
c'est une bonne solution mais je pense que le serveur expirera sur de gros nombres comme 1234567789.
Tahir Raza
5

Cela fera de la conversion (en supposant l'arithmétique entière), mais je suis d'accord avec les autres affiches; juste utiliserbase_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
la source
Cela reviendra Bpour $number = 1au lieu de Adepuis 1 % 26est1
Jalal
5

Réponse tardive, mais voici ce que j'ai fait (pour 1 == A indexé):

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

Ensuite, si vous souhaitez convertir dans l'autre sens:

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;
}
Mike
la source
3

Nombre converti en lettres de colonne 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

Les lettres de la colonne Excel sont converties en nombre:

/**
 * 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
Ann
la source
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
la source
Bien essayé! Cela échouera cependant ... essayez-le avec 2049 et vous verrez: D
Cristian
2

En combinant la réponse récursive d'ircmaxell, j'ai celle-ci:

    function getNameFromNumber ($ num, $ index = 0) {
        $ index = abs ($ index * 1); // assurez-vous que l'index est un entier positif
        $ numeric = ($ num - $ index)% 26; 
        $ lettre = chr (65 + $ numérique);
        $ num2 = intval (($ num - $ index) / 26);
        si ($ num2> 0) {
            renvoie getNameFromNumber ($ num2 - 1 + $ index). $ lettre;
        } autre {
            return $ lettre;
        }
    }

J'utilise l'indexation par défaut basée sur 0, mais cela peut être n'importe quel entier positif pour jongler avec des tableaux en PHP.

Charlie Affumigato
la source
2

Je n'utiliserais jamais cela en production car il n'est pas lisible, mais pour le plaisir ... Seulement jusqu'à ZZ.

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

Pour tous ceux qui recherchent une implémentation Javascript de ceci, voici la réponse 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
la source