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;
}
Cristian
sumber
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 ...

ircmaxell
sumber
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');
ksn135
sumber
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.

Mark Baker
sumber
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;
}
Lucas
sumber
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;
}
Mike
sumber
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
Ann
sumber
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
sumber
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.

Charlie Affumigato
sumber
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);
?>
kesederhanaan
sumber
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;
    }
}
Cels
sumber