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;
}
Может кто-нибудь объяснить мне вышеупомянутую функцию или дать мне некоторое представление о процессе прямого преобразования между произвольными основаниями?
6 ответов
Начиная с PHP 5.3.2, bc_math и gmp теперь поддерживают базы до 62, так что вы можете просто сделать:
echo gmp_strval(gmp_init($mynumber, $srcbase), $destbase);
или эквивалент bc_math.
Пожалуйста, не спрашивайте меня, откуда я это взял, я просто помню, что он основан на некоторых примерах, которые я нашел в Интернете...
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;
}
Самый простой подход для любых задач перевода, от числовой базы до человеческих языков, - это перевод через промежуточный формат.
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 - это.0001100110011...2).
Что касается обобщенного объяснения того, как работает преобразование, рассмотрим, как работают позиционные базовые системы. Цифра вида "an an-1... a1 a0" в основании b представляет число "an* bn + an-1* bn-1 +... + a1* b1 + a0* b0". Преобразование в основном работает, оценивая выражение в контексте другого основания β.
Большинство примеров, которые я нашел в Интернете и в этих ответах, используют функции BC Math. Если вы не хотите использовать функции BC Math, вы можете взглянуть на эту библиотеку: http://www.lalit.org/lab/base62-php-convert-number-to-base-62-for-short-urls/
- Он не использует функции BC Math, поэтому работает без использования библиотеки BC Math.
- Он использует встроенные функции base_convert, когда base ниже 36 для более быстрого выполнения.
- Выходной номер обратно совместим с собственной функцией base_convert.
- Может использоваться для преобразования в и из произвольных оснований между 2-64.
Я писал об использовании функций 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).
(Вы также можете создать комбинированную функцию, которая делает это за один вызов.)
Эта функция выводит то же, что и 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