Bir sayının Excel benzeri sütun adını elde etmek için algoritma


97

Bazı Excel belgeleri oluşturan bir komut dosyası üzerinde çalışıyorum ve bir sayıyı sütun adı eşdeğerine dönüştürmem gerekiyor. Örneğin:

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

Bunu yapmak için zaten bir algoritma yazdım, ancak bunu yapmanın daha basit veya daha hızlı yolları olup olmadığını öğrenmek istiyorum:

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

Bunu yapmanın daha iyi bir yolunu biliyor musun? Belki daha basit tutacak bir şey? veya performans artışı mı?

Düzenle

ircmaxell'in uygulaması oldukça iyi çalışıyor. Ama bu güzel kısa olanı ekleyeceğim:

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


Vaov! Bu kısa bir uygulama. Teşekkür ederim!
Cristian

PHP'den Excel belgeleri oluşturmak için mevcut kitaplıklardan herhangi birine baktınız mı?
Mark Baker

Evet tabi ki. Harika kitaplığınızı kullanıyorum, Mark. Algoritma yazma becerilerimi geliştirmeyi seviyorum ... bunun güzel yanı, birini bitirdikten sonra, tamamen aynı olan ancak daha kısa olan başka algoritmalar bulabilmenizdir.
Cristian

2
Kısa algoritmanız, isteğinizde 1 -> A yerine 0 -> A kullanıyor, istekten biraz farklı ... İstediğiniz buysa, PHPExcel'in PHPExcel_Cell :: stringFromColumnIndex () yöntemine bakın, ancak num2alpha () uygulamanız daha hızlı ol ... Bazı testler yapacağım ve (izninizle) "ödünç alabilirim"
Mark Baker

Yanıtlar:


157

İşte güzel ve basit bir özyinelemeli fonksiyon (Sıfır endeksli sayılara dayalı, yani 0 == A, 1 == B, vb.) ...

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

Ve dizine eklenmesini istiyorsanız (1 == A, vb.):

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'dan 10000'e kadar sayılarla test edilmiştir ...


Teşekkürler! Beni saatte 1/2 kurtardı!
Darryl Hein

Bu PHP komut dosyasını JS'ye
çevirdim

Bir döngüde kullanıldığında, özyineleme işlevinin iyi olup olmadığından şüpheliyim? Yığın alanı sorununa neden olabilir mi?
Scott Chu

95

PhpSpreadsheet kullanma ( PHPExcel kullanımdan kaldırılmıştır )

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

Not endeksi 0 'Z' ile sonuçlanır

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


Doğru yanıt ( PHPExcel Kitaplığı kullanıyorsanız):

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

ve geriye doğru:

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

12
stringFromColumnIndex (1), 'B' değerini döndürür
Ulterior

1
Mükemmel! PHPExcel kullanıyorum. Bu güzel cevap.
Scott Chu

PHPExcel_Cell::stringFromColumnIndex(1)gerçekten döner 'B', lütfen cevabınızı düzenleyin.
MarthyM

13

1 -> A, 2 -> B, vb. İçin indekslenmiştir

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

0 -> A, 1 -> B vb. İçin indekslenmiştir

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

PHP'nin karakter değişkenleri üzerindeki aritmetik işlemlerle uğraşırken C'ler yerine Perl'in kurallarını takip etmesinden yararlanır. Karakter değişkenlerinin artırılabileceğini ancak azaltılamayacağını unutmayın.


iyi bir çözüm ama sanırım 1234567789 gibi büyük sayılarda sunucuda zaman aşımına uğrayacak.
Tahir Raza

5

Bu dönüşüm için yapılacaktır (tamsayı aritmetiği varsayılarak), ancak diğer posterlerle aynı fikirdeyim; sadece kullanbase_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;
}

Bu dönecektir Biçin $number = 1yerine Aberi 1 % 26IS1
Celal

5

Geç cevap, ama yaptığım şey şu (1 == A indeksli için):

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

O zaman diğer yolu dönüştürmek isterseniz:

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

Sayı Excel sütun harflerine dönüştürülür:

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

ör:

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

Excel sütun harfleri Sayıya dönüştürülür:

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

ör:

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

İyi deneme! Yine de başarısız olacak ... 2049 ile bir deneyin ve göreceksiniz: D
Cristian

2

İrcmaxell'in özyinelemeli cevabını birleştirerek şunu elde ettim:

    function getNameFromNumber ($ num, $ indeks = 0) {
        $ endeks = abs ($ dizin * 1); // dizinin pozitif bir tam sayı olduğundan emin olun
        $ sayısal = ($ num - $ dizin)% 26; 
        $ harf = chr (65 + $ sayısal);
        $ num2 = intval (($ num - $ indeks) / 26);
        eğer ($ num2> 0) {
            getNameFromNumber ($ num2 - 1 + $ endeksi) döndür. $ mektup;
        } Başka {
            $ mektup döndür;
        }
    }

Varsayılan indekslemeyi 0 tabanlı olarak kullanıyorum, ancak PHP'deki dizilerle hokkabazlık yaparken herhangi bir pozitif tamsayı olabilir.


2

Bunu üretimde asla kullanmazdım çünkü okunabilir değil, eğlence için ... Sadece ZZ'ye kadar kullanıyor.

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

0

Bir arayan herkese JavaScript bu uygulanmasında, burada @ ircmaxell cevabı içinde 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;
    }
}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.