PHP - Как base_convert() до основания 62

Мне нужна функция base_convert(), которая работает от базы 2 до базы 62, но мне не хватает математики, которую мне нужно использовать, я знаю, что из-за ограничений PHP мне нужно использовать bcmath, и это нормально.

Функции подобные этим преобразуют число в 10-ю систему и наоборот до 62, но я хочу реализовать ту же функциональность, что и base_convert(), например: только одну функцию, которая может конвертировать между произвольными базами.

Я нашел функцию, которая делает это, но это дает мне ощущение, что у меня какой-то избыточный и медленный код, и я хотел бы немного его подправить, если бы знал немецкий, а я этого не знаю. знак равно

Вот более читаемая версия функции:

function bc_base_convert($value, $quellformat, $zielformat)
{
    $vorrat = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    if (min($quellformat, $zielformat) < 2)
    {
        trigger_error('Bad Format min: 2', E_USER_ERROR);
    }

    if (max($quellformat, $zielformat) > strlen($vorrat))
    {
        trigger_error('Bad Format max: ' . strlen($vorrat), E_USER_ERROR);
    }

    $dezi = '0';
    $level = 0;
    $result = '';
    $value = trim(strval($value), "\r\n\t +");
    $vorzeichen = '-' === $value{0} ? '-' : '';
    $value = ltrim($value, "-0");
    $len = strlen($value);

    for ($i = 0; $i < $len; $i++)
    {
        $wert = strpos($vorrat, $value{$len - 1 - $i});

        if (FALSE === $wert)
        {
            trigger_error('Bad Char in input 1', E_USER_ERROR);
        }

        if ($wert >= $quellformat)
        {
            trigger_error('Bad Char in input 2', E_USER_ERROR);
        }

        $dezi = bcadd($dezi, bcmul(bcpow($quellformat, $i), $wert));
    }

    if (10 == $zielformat)
    {
        return $vorzeichen . $dezi; // abkürzung
    }

    while (1 !== bccomp(bcpow($zielformat, $level++), $dezi));

    for ($i = $level - 2; $i >= 0; $i--)
    {
        $factor = bcpow($zielformat, $i);
        $zahl = bcdiv($dezi, $factor, 0);
        $dezi = bcmod($dezi, $factor);
        $result .= $vorrat{$zahl};
    }

    $result = empty($result) ? '0' : $result;

    return $vorzeichen . $result;
}

Может ли кто-нибудь объяснить мне вышеприведенную функцию или пролить свет на процесс прямого преобразования между произвольными базами?


person Alix Axel    schedule 21.12.2009    source источник


Ответы (6)


Начиная с PHP 5.3.2 и bc_math, и gmp теперь поддерживают до 62, так что вы можете просто сделать:

echo gmp_strval(gmp_init($mynumber, $srcbase), $destbase);

или эквивалент bc_math.

person Synchro    schedule 10.08.2011
comment
Это приятно знать, однако я не могу найти эквиваленты BC Math. - person Alix Axel; 11.08.2011
comment
base_convert() не допускает до основания 62 - person dresende; 26.04.2013
comment
Бессовестная самостоятельная вилка, я написал утилиту, которая обрабатывает произвольные числа и основания с использованием GMP, исходники находятся на GitHub: github. com/thunderer/Numbase - person Tomasz Kowalczyk; 24.04.2015

Пожалуйста, не спрашивайте меня, откуда я это взял, я просто помню, что он основан на некоторых примерах, которые я нашел в Интернете...

  function charset_base_convert ($numstring, $fromcharset, $tocharset) {
     $frombase=strlen($fromcharset);
     $tobase=strlen($tocharset);
     $chars = $fromcharset;
     $tostring = $tocharset;

     $length = strlen($numstring);
     $result = '';
     for ($i = 0; $i < $length; $i++) {
         $number[$i] = strpos($chars, $numstring{$i});
     }
     do {
         $divide = 0;
         $newlen = 0;
         for ($i = 0; $i < $length; $i++) {
             $divide = $divide * $frombase + $number[$i];
             if ($divide >= $tobase) {
                 $number[$newlen++] = (int)($divide / $tobase);
                 $divide = $divide % $tobase;
             } elseif ($newlen > 0) {
                 $number[$newlen++] = 0;
             }
         }
         $length = $newlen;
         $result = $tostring{$divide} . $result;
     }
     while ($newlen != 0);
     return $result;
  }
person Florian Fida    schedule 12.01.2011
comment
Очень полезно для преодоления лимита в 62 символа! - person Xeoncross; 08.11.2011
comment
После пары дней поиска того, как преобразовать числовую строку (поскольку PHP анализирует bigint в число с плавающей запятой и становится неточным), это функция, которая выполняет преобразование, которое также можно выполнить в обратном порядке. Например. преобразование $number = "1255276776369394619"; в b4JlnAoHP37 и наоборот. Здорово! - person Avatar; 13.11.2015

Самый простой подход к любым проблемам перевода, от числовой базы до человеческих языков, — это перевод через промежуточный формат.

function bc_base_convert($num, $from, $to) {
    return bc_convert_to(bc_parse_num($num, $from), $to);
}

Теперь все, что вам нужно написать, это bc_convert_to и bc_parse_num. Если платформа различает числовые типы, вам необходимо принять это во внимание. Кроме того, числа с плавающей запятой требуют особого внимания, поскольку число может иметь конечное представление в одной системе счисления, но не в другой (например, 1/3 равно 0,13, но 0,333...10 >, а 1/1010 равно 0,0001100110011...2).

Что касается общего объяснения того, как работает преобразование, рассмотрим, как работают системы позиционной базы. Число вида "anan-1...a1a0" в основание b представляет число "an*bn + an-1*bn -1 + ... + a1*b1 + a0*b0 ". Преобразование в основном работает, оценивая выражение в контексте другого основания.

person outis    schedule 21.12.2009
comment
Извините, но я не понимаю, вы имеете в виду, что я не могу конвертировать, скажем, из базы 61 в базу 5 напрямую? - person Alix Axel; 21.12.2009
comment
Вы могли бы, но проще использовать собственный формат платформы в качестве промежуточной формы. - person outis; 21.12.2009

В большинстве примеров, которые я нашел в Интернете, и в этих ответах используются математические функции BC. Если вы не хотите использовать функции BC Math, вы можете взглянуть на эту библиотеку: http://www.lalit.org/lab/base62-php-convert-number-to-base-62-for-short-urls/

  • Он не использует функции BC Math, поэтому работает без использования библиотеки BC Math.
  • Он использует собственные функции base_convert, когда база меньше 36 для более быстрого выполнения.
  • Выходное число обратно совместимо с собственной функцией base_convert.
  • Может использоваться для преобразования в и из произвольных оснований между 2-64.
person Lalit    schedule 08.12.2011

Я писал об использовании функций BCMath для десятичного/двоичного преобразования здесь: http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ . Вы можете легко изменить этот код для преобразования в разные базы.

Например, в случае преобразования целых чисел измените подпрограммы dec2bin_i() и bin2dec_i(). Переименуйте их и добавьте базовый параметр — что-то вроде dec2base_i($base,$decimal_i) и base2dec_i($base,$num_i), измените жестко заданную «2» на переменную $base, преобразуйте числовые остатки в/из символов базу и переименовать переменные.

Теперь, чтобы преобразовать между произвольными основаниями, используйте десятичное число в качестве промежуточного звена и вызовите обе эти новые функции. Например, преобразуйте число с основанием 42 "123" в число с основанием 59, вызвав $dec = base2dec_i('42','123'), а затем $b59 = dec2base_i(59,$dec).

(Вы также можете создать комбинированную функцию, которая делает это за один вызов.)

person Rick Regan    schedule 21.12.2009
comment
Обратите внимание, что сама base_convert() -- внутри -- использует промежуточную базу, как я отметил здесь: exploringbinary.com/ - person Rick Regan; 21.12.2009

Эта функция выводит то же самое, что и GNU Multiple Precision, если это возможно. …

<?php

function base_convert_alt($val,$from_base,$to_base){
static $gmp;
static $bc;
static $gmp62;
if ($from_base<37) $val=strtoupper($val);
if ($gmp===null) $gmp=function_exists('gmp_init');
if ($gmp62===null) $gmp62=version_compare(PHP_VERSION,'5.3.2')>=0;
if ($gmp && ($gmp62 or ($from_base<37 && $to_base<37)))
return gmp_strval(gmp_init($val,$from_base),$to_base);
if ($bc===null) $bc=function_exists('bcscale');
$range='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($from_base==10)
$base_10=$val;
else
{
$n=strlen(($val="$val"))-++$ratio;
if ($bc) for($i=$n;$i>-1;($ratio=bcmul($ratio,$from_base)) && $i--)
$base_10=bcadd($base_10,bcmul(strpos($range,$val[$i]),$ratio));
else for($i=$n;$i>-1;($ratio*=$from_base) && $i--)
$base_10+=strpos($range,$val[$i])*$ratio;
}
if ($bc)
do $result.=$range[bcmod($base_10,$to_base)];
while(($base_10=bcdiv($base_10,$to_base))>=1);
else
do $result.=$range[$base_10%$to_base];
while(($base_10/=$to_base)>=1);
return strrev($to_base<37?strtolower($result):$result);
}


echo base_convert_alt('2661500360',7,51);

// Output Hello
person Php'Regex    schedule 08.03.2017