Algoritma untuk mendapatkan nama kolom seperti excel dari sebuah angka


95

Saya sedang mengerjakan skrip yang menghasilkan beberapa dokumen Excel dan saya perlu mengonversi angka menjadi nama kolom yang setara. Sebagai contoh:

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

Saya sudah menulis algoritme untuk melakukannya, tetapi saya ingin tahu apakah cara yang lebih sederhana atau lebih cepat untuk melakukannya:

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

Apakah Anda tahu cara yang lebih baik untuk melakukannya? Mungkin sesuatu untuk membuatnya lebih sederhana? atau peningkatan kinerja?

Edit

Implementasi ircmaxell bekerja dengan baik. Tapi, saya akan menambahkan pendek yang bagus ini:

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

3
Komentar pertama di halaman manual ini mungkin berguna: php.net/manual/en/function.base-convert.php#96304
Sergey Eremin

Wow! Itu implementasi singkat. Terima kasih!
Cristian

Pernahkah Anda melihat salah satu pustaka yang ada untuk menghasilkan dokumen Excel dari PHP?
Mark Baker

Ya tentu saja. Saya menggunakan perpustakaan Anda yang luar biasa, Mark. Saya hanya ingin meningkatkan keterampilan saya dalam menulis algoritme ... yang menyenangkan adalah setelah Anda menyelesaikannya, Anda dapat menemukan algoritme lain yang melakukan hal yang persis sama tetapi lebih pendek.
Cristian

2
Algoritme singkat Anda menggunakan 0 -> A daripada 1 -> A dalam permintaan Anda, sedikit berbeda dengan permintaan ... jika itu yang Anda inginkan, lihat metode PHPExcel_Cell :: stringFromColumnIndex () PHPExcel, meskipun implementasi num2alpha () Anda mungkin lebih cepat ... Saya akan menjalankan beberapa tes, dan mungkin "meminjam" (dengan izin)
Mark Baker

Jawaban:


155

Berikut adalah fungsi rekursif sederhana yang bagus (Berdasarkan angka indeks nol, artinya 0 == A, 1 == B, dll) ...

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

Dan jika Anda ingin diindeks (1 == A, dll):

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

Diuji dengan angka dari 0 hingga 10000 ...


Terima kasih! Menyelamatkan saya 1/2 jam!
Darryl Hein

Saya telah menerjemahkan skrip PHP ini ke JS: gist.github.com/terox/161db6259e8ddb56dd77
terox

Saya ragu apakah fungsi recusive baik atau tidak jika digunakan dalam satu lingkaran? Bisakah itu menyebabkan masalah ruang tumpukan?
Scott Chu

93

Menggunakan PhpSpreadsheet ( PHPExcel tidak digunakan lagi )

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

Catatan indeks 0 menghasilkan 'Z'

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


Jawaban yang benar (jika Anda menggunakan PHPExcel Library) adalah:

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

dan mundur:

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

12
stringFromColumnIndex (1) mengembalikan 'B'
Tersembunyi

1
Luar biasa! Saya kebetulan menggunakan PHPExcel. Ini jawaban yang bagus.
Scott Chu

PHPExcel_Cell::stringFromColumnIndex(1)memang kembali 'B', harap edit jawaban Anda.
MarthyM

13

Diindeks untuk 1 -> A, 2 -> B, dll

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

Diindeks untuk 0 -> A, 1 -> B, dll

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

Mengambil keuntungan dari fakta bahwa PHP mengikuti konvensi Perl ketika berhadapan dengan operasi aritmatika pada variabel karakter dan bukan pada C. Perhatikan bahwa variabel karakter bisa bertambah tapi tidak bisa dikurangi.


ini adalah solusi yang baik tetapi saya pikir server akan kehabisan waktu pada nomor besar seperti 1234567789.
Tahir Raza

5

Ini akan dilakukan untuk konversi (dengan asumsi aritmatika integer), tapi saya setuju dengan poster lain; gunakan sajabase_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;
}

Ini akan kembali Buntuk $number = 1bukan Akarena 1 % 26is1
Jalal

5

Jawaban terlambat, tapi inilah yang saya lakukan (untuk 1 == A diindeks):

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

Kemudian jika Anda ingin mengonversi dengan cara lain:

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

Angka diubah menjadi huruf kolom 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

Huruf kolom Excel diubah menjadi angka:

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

Usaha yang bagus! Ini akan gagal ... mencobanya dengan 2049 dan Anda akan melihat: D
Cristian

2

Menggabungkan jawaban rekursif ircmaxell, saya punya yang ini:

    function getNameFromNumber ($ num, $ index = 0) {
        $ index = abs ($ index * 1); // pastikan indeks adalah bilangan bulat positif
        $ numerik = ($ num - $ indeks)% 26; 
        $ letter = chr (65 + $ numerik);
        $ num2 = intval (($ num - $ indeks) / 26);
        if ($ num2> 0) {
            mengembalikan getNameFromNumber ($ num2 - 1 + $ indeks). $ surat;
        } lain {
            mengembalikan $ letter;
        }
    }

Saya menggunakan pengindeksan default sebagai berbasis 0, tetapi ini bisa berupa bilangan bulat positif saat menyulap array di PHP.


2

Saya tidak pernah menggunakan ini dalam produksi karena tidak dapat dibaca, tapi untuk kesenangan ... Hanya sampai ZZ.

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

0

Untuk siapa pun yang mencari implementasi Javascript ini, berikut adalah jawaban @ ircmaxell di 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;
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.