Как мне создать сокращатель URL?

Я хочу создать службу сокращения URL, где вы можете записать длинный URL в поле ввода, а служба сокращает URL до " http://www.example.org/abcdef ".

Вместо " abcdef "может быть любая другая строка с шестью символами, содержащая a-z, A-Z and 0-9, Это составляет 56~57 миллиардов возможных строк.

Мой подход:

У меня есть таблица базы данных с тремя столбцами:

  1. id, целое число, автоинкремент
  2. long, string, длинный URL, введенный пользователем
  3. короткий, строка, сокращенный URL (или только шесть символов)

Затем я вставил бы длинный URL в таблицу. Тогда я бы выбрал значение автоинкремента для id "и построить его хэш. Этот хэш должен быть вставлен как" short "Но какой тип хэша я должен создать? Алгоритмы хеширования, такие как MD5, создают слишком длинные строки. Я думаю, что я не использую эти алгоритмы. Я думаю, что будет работать и самосозданный алгоритм.

Моя идея:

За " http://www.google.de/ "Я получаю идентификатор автоинкремента 239472, Затем я делаю следующие шаги:

short = '';
if divisible by 2, add "a"+the result to short
if divisible by 3, add "b"+the result to short
... until I have divisors for a-z and A-Z.

Это может повторяться до тех пор, пока число больше не будет делиться. Как вы думаете, это хороший подход? У тебя есть идея получше?

Из-за постоянного интереса к этой теме я опубликовал эффективное решение для GitHub с реализациями для JavaScript, PHP, Python и Java. Добавьте ваши решения, если хотите:)

27 ответов

Решение

Я бы продолжил ваш подход "конвертировать число в строку". Однако вы поймете, что предложенный вами алгоритм не работает, если ваш идентификатор простое и больше 52.

Теоретические основы

Вам нужна биективная функция f. Это необходимо для того, чтобы вы могли найти обратную функцию g('abc') = 123 для вашей функции f(123) = 'abc'. Это означает:

  • Не должно быть x1, x2 (с x1 ≠ x2), из-за которых f (x1) = f (x2),
  • и для каждого y вы должны быть в состоянии найти x, так что f (x) = y.

Как преобразовать идентификатор в сокращенный URL

  1. Подумайте об алфавите, который мы хотим использовать. В вашем случае это [a-zA-Z0-9], Он содержит 62 буквы.
  2. Возьмите автоматически сгенерированный, уникальный числовой ключ (автоинкрементный id таблицы MySQL например).

    Для этого примера я буду использовать 12510 (125 с основанием 10).

  3. Теперь вам нужно конвертировать 12510 в X62 (база 62).

    12510 = 2 × 621 + 1 × 620 = [2,1]

    Это требует использования целочисленного деления и по модулю. Пример псевдокода:

    digits = []
    
    while num > 0
      remainder = modulo(num, 62)
      digits.push(remainder)
      num = divide(num, 62)
    
    digits = digits.reverse
    

    Теперь сопоставьте индексы 2 и 1 с вашим алфавитом. Вот как может выглядеть ваше отображение (например, с массивом):

    0  → a
    1  → b
    ...
    25 → z
    ...
    52 → 0
    61 → 9
    

    При 2 → c и 1 → b вы получите cb62 в качестве сокращенного URL.

    http://shor.ty/cb
    

Как разрешить сокращенный URL к начальному идентификатору

Обратное еще проще. Вы просто делаете обратный поиск в вашем алфавите.

  1. e9a62 будет преобразован в "4-ю, 61-ю и 0-ю букву в алфавите".

    с9а62 = [4,61,0] = 4 × 622 + 61 × 621 + 0 × 620 = 1915810

  2. Теперь найдите свою базу данных-запись с WHERE id = 19158 и сделать перенаправление.

Некоторые реализации (предоставленные комментаторами)

Почему вы хотите использовать хеш?

Вы можете просто использовать простой перевод значения автоинкремента в буквенно-цифровое значение. Вы можете сделать это легко с помощью некоторого базового преобразования. Допустим, у вас есть пространство символов (AZ, az, 0-9 и т. Д.), Состоящее из 40 символов, преобразуйте идентификатор в число с основанием 40 и используйте символы в качестве цифр.

public class UrlShortener {
    private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static final int    BASE     = ALPHABET.length();

    public static String encode(int num) {
        StringBuilder sb = new StringBuilder();
        while ( num > 0 ) {
            sb.append( ALPHABET.charAt( num % BASE ) );
            num /= BASE;
        }
        return sb.reverse().toString();   
    }

    public static int decode(String str) {
        int num = 0;
        for ( int i = 0; i < str.length(); i++ )
            num = num * BASE + ALPHABET.indexOf(str.charAt(i));
        return num;
    }   
}

Не ответ на ваш вопрос, но я бы не стал использовать сокращенные URL-адреса с учетом регистра. Их трудно запомнить, обычно они не читаются (многие шрифты отображают 1 и l, 0 и O и другие символы очень похожи, так что почти невозможно различить их), и подвержены ошибкам. Попробуйте использовать только нижний или верхний регистр.

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

Мой подход: взять идентификатор базы данных, затем Base36 кодировать его. Я НЕ буду использовать как заглавные, так и строчные буквы, потому что это делает передачу этих URL-адресов по телефону кошмаром, но вы, конечно, можете легко расширить эту функцию до базового 62 en/ декодера.

Вот мой класс PHP 5.

<?php
class Bijective
{
    public $dictionary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    public function __construct()
    {
        $this->dictionary = str_split($this->dictionary);
    }

    public function encode($i)
    {
        if ($i == 0)
        return $this->dictionary[0];

        $result = '';
        $base = count($this->dictionary);

        while ($i > 0)
        {
            $result[] = $this->dictionary[($i % $base)];
            $i = floor($i / $base);
        }

        $result = array_reverse($result);

        return join("", $result);
    }

    public function decode($input)
    {
        $i = 0;
        $base = count($this->dictionary);

        $input = str_split($input);

        foreach($input as $char)
        {
            $pos = array_search($char, $this->dictionary);

            $i = $i * $base + $pos;
        }

        return $i;
    }
}

Решение Node.js и MongoDB

Так как мы знаем формат, который MongoDB использует для создания нового ObjectId с 12 байтами.

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

Пример (я выбираю случайную последовательность)a1b2c3d4e5f6g7h8i9j1k2l3

  • a1b2c3d4 представляет секунды с начала эпохи Unix,
  • 4e5f6g7 представляет идентификатор машины,
  • h8i9 представляет идентификатор процесса
  • j1k2l3 представляет счетчик, начиная со случайного значения.

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

Таким образом, короткий URL будет счетчиком, а вот фрагмент кода, предполагающий, что ваш сервер работает правильно.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// Create a schema
const shortUrl = new Schema({
    long_url: { type: String, required: true },
    short_url: { type: String, required: true, unique: true },
  });
const ShortUrl = mongoose.model('ShortUrl', shortUrl);

// The user can request to get a short URL by providing a long URL using a form

app.post('/shorten', function(req ,res){
    // Create a new shortUrl */
    // The submit form has an input with longURL as its name attribute.
    const longUrl = req.body["longURL"];
    const newUrl = ShortUrl({
        long_url : longUrl,
        short_url : "",
    });
    const shortUrl = newUrl._id.toString().slice(-6);
    newUrl.short_url = shortUrl;
    console.log(newUrl);
    newUrl.save(function(err){
        console.log("the new URL is added");
    })
});

Я продолжаю увеличивать целочисленную последовательность для каждого домена в базе данных и использую Hashids для кодирования целого числа в URL-пути.

static hashids = Hashids(salt = "my app rocks", minSize = 6)

Я запустил скрипт, чтобы увидеть, сколько времени потребуется, чтобы исчерпать длину символа. Для шести символов это может сделать 164,916,224 ссылки, а затем идет до семи символов. Битли использует семь символов. Под пятью персонажами выглядит странно для меня.

Хашиды могут декодировать путь URL обратно к целому числу, но более простым решением является использование всей короткой ссылки sho.rt/ka8ds3 в качестве первичного ключа.

Вот полная концепция:

function addDomain(domain) {
    table("domains").insert("domain", domain, "seq", 0)
}

function addURL(domain, longURL) {
    seq = table("domains").where("domain = ?", domain).increment("seq")
    shortURL = domain + "/" + hashids.encode(seq)
    table("links").insert("short", shortURL, "long", longURL)
    return shortURL
}

// GET /:hashcode
function handleRequest(req, res) {
    shortURL = req.host + "/" + req.param("hashcode")
    longURL = table("links").where("short = ?", shortURL).get("long")
    res.redirect(301, longURL)
}

Взгляните на https://hashids.org/, это открытый исходный код на многих языках.

На их странице описаны некоторые подводные камни других подходов.

C# версия:

public class UrlShortener 
{
    private static String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static int    BASE     = 62;

    public static String encode(int num)
    {
        StringBuilder sb = new StringBuilder();

        while ( num > 0 )
        {
            sb.Append( ALPHABET[( num % BASE )] );
            num /= BASE;
        }

        StringBuilder builder = new StringBuilder();
        for (int i = sb.Length - 1; i >= 0; i--)
        {
            builder.Append(sb[i]);
        }
        return builder.ToString(); 
    }

    public static int decode(String str)
    {
        int num = 0;

        for ( int i = 0, len = str.Length; i < len; i++ )
        {
            num = num * BASE + ALPHABET.IndexOf( str[(i)] ); 
        }

        return num;
    }   
}

Вы можете хэшировать весь URL, но если вы просто хотите сократить идентификатор, сделайте, как предложил Марсель. Я написал эту реализацию Python:

https://gist.github.com/778542

// simple approach

$original_id = 56789;

$shortened_id = base_convert($original_id, 10, 36);

$un_shortened_id = base_convert($shortened_id, 36, 10);

Если вы не хотите заново изобретать колесо... http://lilurl.sourceforge.net/

alphabet = map(chr, range(97,123)+range(65,91)) + map(str,range(0,10))

def lookup(k, a=alphabet):
    if type(k) == int:
        return a[k]
    elif type(k) == str:
        return a.index(k)


def encode(i, a=alphabet):
    '''Takes an integer and returns it in the given base with mappings for upper/lower case letters and numbers 0-9.'''
    try:
        i = int(i)
    except Exception:
        raise TypeError("Input must be an integer.")

    def incode(i=i, p=1, a=a):
        # Here to protect p.                                                                                                                                                                                                                
        if i <= 61:
            return lookup(i)

        else:
            pval = pow(62,p)
            nval = i/pval
            remainder = i % pval
            if nval <= 61:
                return lookup(nval) + incode(i % pval)
            else:
                return incode(i, p+1)

    return incode()



def decode(s, a=alphabet):
    '''Takes a base 62 string in our alphabet and returns it in base10.'''
    try:
        s = str(s)
    except Exception:
        raise TypeError("Input must be a string.")

    return sum([lookup(i) * pow(62,p) for p,i in enumerate(list(reversed(s)))])a

Вот моя версия для тех, кому это нужно.

/**
 * <p>
 *     Integer to character and vice-versa
 * </p>
 *  
 */
public class TinyUrl {

    private final String characterMap = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private final int charBase = characterMap.length();

    public String covertToCharacter(int num){
        StringBuilder sb = new StringBuilder();

        while (num > 0){
            sb.append(characterMap.charAt(num % charBase));
            num /= charBase;
        }

        return sb.reverse().toString();
    }

    public int covertToInteger(String str){
        int num = 0;
        for(int i = 0 ; i< str.length(); i++)
            num += characterMap.indexOf(str.charAt(i)) * Math.pow(charBase , (str.length() - (i + 1)));

        return num;
    }
}

class TinyUrlTest{

    public static void main(String[] args) {
        TinyUrl tinyUrl = new TinyUrl();
        int num = 122312215;
        String url = tinyUrl.covertToCharacter(num);
        System.out.println("Tiny url:  " + url);
        System.out.println("Id: " + tinyUrl.covertToInteger(url));
    }
}

Почему бы просто не перевести свой идентификатор в строку? Вам просто нужна функция, которая отображает цифру, скажем, от 0 до 61, в одну букву (верхний / нижний регистр) или цифру. Затем примените это для создания, скажем, четырехбуквенных кодов, и вы получите 14,7 миллионов URL-адресов.

Не знаю, найдет ли кто-нибудь это полезным - это скорее метод 'hack n slash', но он прост и отлично работает, если вам нужны только определенные символы.

$dictionary = "abcdfghjklmnpqrstvwxyz23456789";
$dictionary = str_split($dictionary);

// Encode
$str_id = '';
$base = count($dictionary);

while($id > 0) {
    $rem = $id % $base;
    $id = ($id - $rem) / $base;
    $str_id .= $dictionary[$rem];
}


// Decode
$id_ar = str_split($str_id);
$id = 0;

for($i = count($id_ar); $i > 0; $i--) {
    $id += array_search($id_ar[$i-1], $dictionary) * pow($base, $i - 1);
} 

Вы пропустили O, 0, а я специально?

Я только что создал класс PHP на основе решения Райана.

<?php

    $shorty = new App_Shorty();

    echo 'ID: ' . 1000;
    echo '<br/> Short link: ' . $shorty->encode(1000);
    echo '<br/> Decoded Short Link: ' . $shorty->decode($shorty->encode(1000));


    /**
     * A nice shorting class based on Ryan Charmley's suggestion see the link on Stack Overflow below.
     * @author Svetoslav Marinov (Slavi) | http://WebWeb.ca
     * @see http://stackru.com/questions/742013/how-to-code-a-url-shortener/10386945#10386945
     */
    class App_Shorty {
        /**
         * Explicitly omitted: i, o, 1, 0 because they are confusing. Also use only lowercase ... as
         * dictating this over the phone might be tough.
         * @var string
         */
        private $dictionary = "abcdfghjklmnpqrstvwxyz23456789";
        private $dictionary_array = array();

        public function __construct() {
            $this->dictionary_array = str_split($this->dictionary);
        }

        /**
         * Gets ID and converts it into a string.
         * @param int $id
         */
        public function encode($id) {
            $str_id = '';
            $base = count($this->dictionary_array);

            while ($id > 0) {
                $rem = $id % $base;
                $id = ($id - $rem) / $base;
                $str_id .= $this->dictionary_array[$rem];
            }

            return $str_id;
        }

        /**
         * Converts /abc into an integer ID
         * @param string
         * @return int $id
         */
        public function decode($str_id) {
            $id = 0;
            $id_ar = str_split($str_id);
            $base = count($this->dictionary_array);

            for ($i = count($id_ar); $i > 0; $i--) {
                $id += array_search($id_ar[$i - 1], $this->dictionary_array) * pow($base, $i - 1);
            }
            return $id;
        }
    }
?>

Вот достойная функция кодирования URL для PHP...

// From http://snipplr.com/view/22246/base62-encode--decode/
private function base_encode($val, $base=62, $chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
    $str = '';
    do {
        $i = fmod($val, $base);
        $str = $chars[$i] . $str;
        $val = ($val - $i) / $base;
    } while($val > 0);
    return $str;
}

Реализация в Scala:

class Encoder(alphabet: String) extends (Long => String) {

  val Base = alphabet.size

  override def apply(number: Long) = {
    def encode(current: Long): List[Int] = {
      if (current == 0) Nil
      else (current % Base).toInt :: encode(current / Base)
    }
    encode(number).reverse
      .map(current => alphabet.charAt(current)).mkString
  }
}

class Decoder(alphabet: String) extends (String => Long) {

  val Base = alphabet.size

  override def apply(string: String) = {
    def decode(current: Long, encodedPart: String): Long = {
      if (encodedPart.size == 0) current
      else decode(current * Base + alphabet.indexOf(encodedPart.head),encodedPart.tail)
    }
    decode(0,string)
  }
}

Тестовый пример с тестом Scala:

import org.scalatest.{FlatSpec, Matchers}

class DecoderAndEncoderTest extends FlatSpec with Matchers {

  val Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

  "A number with base 10" should "be correctly encoded into base 62 string" in {
    val encoder = new Encoder(Alphabet)
    encoder(127) should be ("cd")
    encoder(543513414) should be ("KWGPy")
  }

  "A base 62 string" should "be correctly decoded into a number with base 10" in {
    val decoder = new Decoder(Alphabet)
    decoder("cd") should be (127)
    decoder("KWGPy") should be (543513414)
  }

}

Качественное решение Node.js / JavaScript см. В модуле id-shorttener, который тщательно протестирован и уже несколько месяцев используется в производстве.

Он обеспечивает эффективное сокращение идентификатора / URL-адреса при поддержке подключаемого хранилища по умолчанию Redis, и вы даже можете настроить свой короткий набор символов идентификатора и определить, является ли сокращение идемпотентным. Это важное различие, которое учитывают не все средства сокращения URL.

В отношении других ответов здесь, этот модуль реализует превосходный принятый ответ Марселя Джекверта выше.

Основу решения предоставляет следующий фрагмент Redis Lua:

local sequence = redis.call('incr', KEYS[1])

local chars = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz'
local remaining = sequence
local slug = ''

while (remaining > 0) do
  local d = (remaining % 60)
  local character = string.sub(chars, d + 1, d + 1)

  slug = character .. slug
  remaining = (remaining - d) / 60
end

redis.call('hset', KEYS[2], slug, ARGV[1])

return slug

Моя версия Python 3

base_list = list("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
base = len(base_list)

def encode(num: int):
    result = []
    if num == 0:
        result.append(base_list[0])

    while num > 0:
        result.append(base_list[num % base])
        num //= base

    print("".join(reversed(result)))

def decode(code: str):
    num = 0
    code_list = list(code)
    for index, code in enumerate(reversed(code_list)):
        num += base_list.index(code) * base ** index
    print(num)

if __name__ == '__main__':
    encode(341413134141)
    decode("60FoItT")

Вот реализация Node.js, которая, вероятно, будет bit.ly. создать очень случайную строку из семи символов.

Он использует криптографию Node.js для генерации очень случайного набора символов из 25 символов, а не случайного выбора семи символов.

var crypto = require("crypto");
exports.shortURL = new function () {
    this.getShortURL = function () {
        var sURL = '',
            _rand = crypto.randomBytes(25).toString('hex'),
            _base = _rand.length;
        for (var i = 0; i < 7; i++)
            sURL += _rand.charAt(Math.floor(Math.random() * _rand.length));
        return sURL;
    };
}

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

Это мои первоначальные мысли, и можно еще подумать или провести имитацию, чтобы увидеть, хорошо ли это работает или требуется какое-либо улучшение:

Мой ответ - запомнить длинный URL-адрес в базе данных и использовать идентификатор 0 к 9999999999999999 (или сколько бы ни было необходимо).

Но ID от 0 до 9999999999999999 может быть проблемой, потому что

  1. он может быть короче, если мы используем шестнадцатеричный код или даже base62 или base64. (base64, как и YouTube, используяA-Z a-z 0-9 _ а также -)
  2. если он увеличивается с 0 к 9999999999999999 единообразно, тогда хакеры могут посещать их в указанном порядке и знать, какие URL-адреса люди отправляют друг другу, так что это может быть проблемой конфиденциальности

Мы можем это сделать:

  1. выделить один сервер 0 к 999к одному серверу, серверу A, поэтому теперь у сервера A 1000 таких идентификаторов. Таким образом, если 20 или 200 серверов постоянно нуждаются в новых идентификаторах, ему не нужно постоянно запрашивать каждый новый идентификатор, а скорее запрашивать один раз 1000 идентификаторов.
  2. для ID 1, например, поменять местами биты. Так 000...00000001 становится 10000...000, поэтому при преобразовании в base64 он будет неравномерно увеличивать идентификаторы каждый раз.
  3. используйте XOR, чтобы перевернуть биты для окончательных идентификаторов. Например, XOR с0xD5AA96...2373(как секретный ключ), и некоторые биты будут перевернуты. (всякий раз, когда секретный ключ имеет бит 1, он перевернет бит идентификатора). В результате идентификаторы будет труднее угадать, и они будут казаться более случайными.

Следуя этой схеме, один сервер, который выделяет идентификаторы, может формировать идентификаторы, а также 20 или 200 серверов, запрашивающих выделение идентификаторов. Выделяющий сервер должен использовать блокировку / семафор, чтобы два запрашивающих сервера не могли получить один и тот же пакет (или, если он принимает одно соединение за раз, это уже решает проблему). Поэтому мы не хотим, чтобы очередь (очередь) была слишком длинной для ожидания получения распределения. Вот почему выделение 1000 или 10000 за раз может решить проблему.

У меня есть вариант проблемы в том, что я храню веб-страницы от разных авторов, и мне нужно предотвратить обнаружение страниц путем догадок. Поэтому мои короткие URL-адреса добавляют пару дополнительных цифр в строку Base-62 для номера страницы. Эти дополнительные цифры генерируются из информации в самой записи страницы, и они гарантируют, что только 1 из 3844 URL-адресов являются действительными (при условии 2-значный Base-62). Вы можете увидеть общее описание на http://mgscan.com/MBWL.

Вот тот, который я создал и развернул в консоли Google Cloud. Он написан на Java и Spring boot.

это https://jol.ink

Если вам нужны подробности, просто дайте мне знать в разделе комментариев, я отредактирую этот пост и подробно объясню его

Почему бы просто не сгенерировать случайную строку и не добавить ее к базовому URL-адресу? Это очень упрощенная версия этого на C#.

static string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static string baseUrl = "https://google.com/";

private static string RandomString(int length)
{
    char[] s = new char[length];
    Random rnd = new Random();
    for (int x = 0; x < length; x++)
    {
        s[x] = chars[rnd.Next(chars.Length)];
    }
    Thread.Sleep(10);

    return new String(s);
}

Затем просто добавьте случайную строку к baseURL:

string tinyURL = baseUrl + RandomString(5);

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

Очень хороший ответ, я создал реализацию bjf на Golang:

package bjf

import (
    "math"
    "strings"
    "strconv"
)

const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func Encode(num string) string {
    n, _ := strconv.ParseUint(num, 10, 64)
    t := make([]byte, 0)

    /* Special case */
    if n == 0 {
        return string(alphabet[0])
    }

    /* Map */
    for n > 0 {
        r := n % uint64(len(alphabet))
        t = append(t, alphabet[r])
        n = n / uint64(len(alphabet))
    }

    /* Reverse */
    for i, j := 0, len(t) - 1; i < j; i, j = i + 1, j - 1 {
        t[i], t[j] = t[j], t[i]
    }

    return string(t)
}

func Decode(token string) int {
    r := int(0)
    p := float64(len(token)) - 1

    for i := 0; i < len(token); i++ {
        r += strings.Index(alphabet, string(token[i])) * int(math.Pow(float64(len(alphabet)), p))
        p--
    }

    return r
}

Размещено на github: https://github.com/xor-gate/go-bjf

Функция основана на классе Xeoncross

function shortly($input){
$dictionary = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9'];
if($input===0)
    return $dictionary[0];
$base = count($dictionary);
if(is_numeric($input)){
    $result = [];
    while($input > 0){
        $result[] = $dictionary[($input % $base)];
        $input = floor($input / $base);
    }
    return join("", array_reverse($result));
}
$i = 0;
$input = str_split($input);
foreach($input as $char){
    $pos = array_search($char, $dictionary);
    $i = $i * $base + $pos;
}
return $i;
}
Другие вопросы по тегам