117 votes

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

Je travaille sur un script qui génère certains documents Excel et j'ai besoin de convertir un nombre en son équivalent en 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 un meilleur moyen de le faire ? Peut-être quelque chose de plus simple ? ou une amélioration des performances ?

Modifier

L'implémentation d'ircmaxell fonctionne très bien. Mais, je vais ajouter ce petit truc sympa :

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

3 votes

Le premier commentaire de cette page de manuel pourrait être utile : php.net/manual/fr/function.base-convert.php#96304

0 votes

Wow ! C'est une courte mise en œuvre. Merci !

0 votes

Avez-vous examiné les bibliothèques existantes pour générer des documents Excel à partir de PHP ?

176voto

ircmaxell Points 74865

Voici une jolie fonction récursive simple (basée sur des nombres indexés sur zéro, c'est-à-dire 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 voulez qu'il soit 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...

1 votes

Merci ! J'ai gagné une demi-heure !

0 votes

J'ai traduit ce script PHP en JS : gist.github.com/terox/161db6259e8ddb56dd77

0 votes

Je doute que la fonction récurrente soit bonne ou non si elle est utilisée dans une boucle ? Peut-elle causer un problème d'espace de pile ?

127voto

ksn135 Points 61

Utilisation de PhpSpreadsheet ( PHPExcel est déprécié )

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

Note : l'indice 0 donne lieu à un 'Z'.

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


La réponse correcte (si vous utilisez PHPExcel Bibliothèque) est :

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

et en arrière :

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

12 votes

StringFromColumnIndex(1) renvoie 'B'.

1 votes

Excellent ! Il se trouve que j'utilise PHPExcel. C'est une réponse claire.

0 votes

PHPExcel_Cell::stringFromColumnIndex(1) en effet, les rendements 'B' veuillez modifier votre réponse.

13voto

Mark Baker Points 90240

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

Tire parti du fait que PHP suit la convention de Perl pour les opérations arithmétiques sur les variables de type caractère, et non celle du C. Notez que les variables de type caractère peuvent être incrémentées mais pas décrémentées.

0 votes

C'est une bonne solution mais je pense qu'elle va faire perdre le temps au serveur pour les grands nombres comme 1234567789.

6voto

Lucas Points 4126

Cela fera la conversion (en supposant une arithmétique entière), mais je suis d'accord avec les autres posters ; utilisez simplement 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;
}

0 votes

Cela donnera B para $number = 1 au lieu de A depuis 1 % 26 es 1

6voto

Mike Points 5108

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X