Як створити скорочувач URL-адрес?


667

Я хочу створити службу скорочення 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 . Додайте свої рішення, якщо вам подобається :)


5
@gudge Суть цих функцій полягає в тому, що вони мають зворотну функцію. Це означає, що ви можете мати encode()і decode()функції, і функції. Отже, такі кроки: (1) Збереження URL-адреси в базі даних (2) Отримати унікальний ідентифікатор рядка для цієї URL-адреси з бази даних (3) Перетворення цілого ідентифікатора в короткий рядок encode(), наприклад, 273984в f5a4(4) Використовуйте короткий рядок (наприклад f4a4) у вашому спільні URL-адреси (5) Отримуючи запит на короткий рядок (наприклад 20a8), розшифруйте рядок до цілого ідентифікатора з decode()(6) Знайдіть URL-адресу в базі даних для вказаного ідентифікатора. Для перетворення використовуйте: github.com/delight-im/ShortURL
caw

@Marco, який сенс зберігати хеш у базі даних?
Максим Ві.

3
@MaksimVi. Якщо у вас є зворотна функція, її немає. Якби у вас була одностороння хеш-функція, вона була б такою.
каре

1
було б неправильно, якби ми використовували простий алгоритм CRC32 для скорочення URL-адреси? Хоча це малоймовірно зіткнення (вихід CRC32 зазвичай становить 8 символів і це дає нам більше 30 мільйонів можливостей). Якщо генерований вихід CRC32 вже використовувався раніше і був знайдений у базі даних, ми могли б залишити довгу URL-адресу випадковим числом поки ми не знайдемо вихід CRC32, який є унікальним у моїй базі даних. Наскільки поганим чи різним чи потворним це було б для простого рішення?
Ракіб

Відповіді:


816

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

Теоретичне тло

Вам потрібна функція Bijective f . Це необхідно, щоб ви могли знайти зворотну функцію g ('abc') = 123 для вашої функції f (123) = 'abc' . Це означає:

  • Не повинно бути x1, x2 (з x1 ≠ x2), що зробить f (x1) = f (x2) ,
  • і для кожного y ви повинні вміти знаходити х, щоб f (x) = y .

Як конвертувати ідентифікатор у скорочений URL

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

    Для цього прикладу я буду використовувати 125 10 (125 з основою 10).

  3. Тепер вам доведеться перетворити 125 10 в X 62 (база 62).

    125 10 = 2 × 62 1 + 1 × 62 0 =[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 ви отримаєте cb 62 як скорочену URL-адресу.

    http://shor.ty/cb
    

Як вирішити скорочену URL-адресу до початкового ідентифікатора

Реверс ще простіше. Ви просто зробите зворотний пошук у своєму алфавіті.

  1. e9a 62 буде вирішено на "4- ту , 61-ю та 0-ю літери в алфавіті".

    e9a 62 = [4,61,0]= 4 × 62 2 + 61 × 62 1 + 0 × 62 0 = 19158 10

  2. Тепер знайдіть свою базу даних із записом WHERE id = 19158та виконайте переадресацію.

Приклад реалізації (надається коментаторами)


18
Не забудьте очистити URL-адреси від шкідливого коду JavaScript! Пам’ятайте, що JavaScript може бути базовим64, закодованим у URL-адресі, тому просто пошук 'javascript' недостатньо хороший.j
Bjorn

3
Функція повинна бути біективною (ін'єкційною та сюжективною), щоб мати зворотну.
Gumbo

57
Їжа для роздумів, можливо, буде корисно додати URL-адресу контрольної суми з двома символами. Це запобіжить прямій ітерації всіх URL-адрес у вашій системі. Щось таке, як f (checkum (id)% (62 ^ 2)) + f (id) = url_id
koblas

6
Що стосується санітарної обробки URL-адрес, одна з проблем, з якою ви зіткнетеся, - це спамери, які використовують ваш сервіс, щоб замаскувати їх URL-адреси, щоб уникнути спам-фільтрів. Вам потрібно або обмежити послугу відомими хорошими акторами, або застосувати фільтрацію спаму до довгих URL-адрес. Інакше ви будете зловживати спамерами.
Едвард Фолк

74
Base62 може бути поганим вибором, оскільки він має потенціал для генерування f * слів (наприклад, 3792586=='F_ck'з i на місці _). Я б виключив деякі символи, такі як u / U, щоб мінімізувати це.
Пауло Скардін

56

Навіщо ви хочете використовувати хеш?

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


13
осторонь того, що AZ, az і 0-9 = 62 знаки, а не 40, ви прямо на оцінці.
Еван Теран

Дякую! Чи слід тоді використовувати алфавіт основи-62? en.wikipedia.org/wiki/Base_62 Але як я можу перетворити ідентифікатори на номер base-62?
каре

Використання базового алгоритму перетворення
курсу

2
Що стосується "Чому ви хочете використовувати хеш?", Базове перетворення, засноване на автоматичному збільшенні, створить послідовні URL-адреси, тому вам потрібно буде комфортно, щоб люди могли "переглядати" скорочені URL-адреси інших людей, правильно?
Ендрю Коулсон

2
маючи достатньо ресурсів і часу, ви можете "переглянути" всі URL-адреси будь-якої служби скорочення URL-адрес.
shoosh

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

Мені дуже подобається ідея, єдина проблема, яку я маю з нею, полягає в тому, що я продовжую виводити змінну num у функції декодування поза межами (навіть надовго), чи є у вас ідеї, як змусити її працювати? чи це лише теоретичне?
користувач1322801

@ user1322801: Імовірно, ви намагаєтесь розшифрувати щось, що було набагато більше, ніж те, що може реально працювати функція кодування. Ви можете отримати трохи більше пробігу, якщо перетворити всі "ints" на BigInteger, але якщо ви не отримали> 9223372036854775807 індексів, довго, мабуть, вистачить.
biggusjimmus

2
Чи можу я знати, яке значення заднього ходу? тобто sb.reverse (). toString ();
Декодер

Це що 62 ^ 62 = 1,7 трлн?
Ной Тоні

33

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

Також спробуйте мати формат, у якому ви змішуєте числа та символи заздалегідь визначеною формою. Існують дослідження, які показують, що люди, як правило, запам'ятовують одну форму краще, ніж інші (подумайте номери телефонів, де номери згруповані в певну форму). Спробуйте щось на кшталт num-char-char-num-char-char. Я знаю, що це знизить комбінації, особливо якщо у вас немає верхнього та нижнього регістру, але це було б більш корисно і тому корисно.


2
Дякую, дуже гарна ідея. Я про це ще не думав. Зрозуміло, що це залежить від виду використання, має це сенс чи ні.
каре

19
Це не буде проблемою, якщо люди строго копіюють та вставляють короткі URL-адреси.
Едвард Фолк

2
Метою коротких URL-адрес є не запам'ятовувати чи просто говорити. Це лише натиснути або скопіювати / вставити.
Гюго Ногейра

так, я подумав, що коротка URL-адреса призначена лише для того, щоб люди перераховували її чи
надсилали

29

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


Спасибі, ти маєш рацію. Якщо у вас є 2,176,782,336 можливостей або 56 800,235,584, це те саме: обох буде достатньо. Тож я буду використовувати кодування бази 36.
каре

Це може бути очевидним, але ось деякий код PHP, на який посилається у wikipedia, кодування base64 в php tonymarston.net/php-mysql/converter.html
Райан Уайт

8

Ось мій 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;
    }
}

6

Рішення Node.js і MongoDB

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

  • 4-байтне значення, що представляє секунди після епохи Unix,
  • 3-байтний ідентифікатор машини,
  • 2-байтний ідентифікатор процесу
  • 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");
    })
});

1
Як RDBMS може бути кращим, ніж магазин без sql / key-value?
kjs3

@ kjs3 так, ти маєш рацію, оскільки немає зв’язків з іншими таблицями, немає потреби в RDBMS і зберігання ключових значень буде швидше.
Фірас Омране

4

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

4

Ви можете зафіксувати всю URL-адресу, але якщо ви просто хочете скоротити ідентифікатор, зробіть так, як запропонував marcel. Я написав цю реалізацію Python:

https://gist.github.com/778542


4

Я продовжую збільшувати цілу послідовність на домен в базі даних і використовую 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)
}


3
// simple approach

$original_id = 56789;

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

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

2
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

Ось моя версія для тих, хто її потребує.


1

Чому б просто не перекласти свій ідентифікатор у рядок? Вам просто потрібна функція, яка відображає цифру між, скажімо, 0 і 61, до однієї літери (верхнього / нижнього регістру) або цифри. Потім застосуйте це, щоб створити, скажімо, 4-літерні коди, і у вас є 14,7 мільйонів URL-адрес.


+1 для спрощеного мислення. Це дійсно так просто. Я щойно опублікував відповідь, яка робить саме це. У мене є деякий виробничий код, який запитує базу даних, щоб переконатися, що немає повторюваних рядків, і все є унікальним.
Ендрю Різ

1

Ось гідна функція кодування 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;
}

1

Не знаю, чи знайде хтось це корисним - це скоріше метод "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);
} 

1

Ви пропустили О, 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://stackoverflow.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;
        }
    }
?>

Так. Ви бачили коментар трохи нижче декларації класу?
Светослав Маринов

1

Погляньте на https://hashids.org/ це відкритий код та багатьма мовами.

На їхній сторінці викладені деякі підводні камені інших підходів.


0

Це те, що я використовую:

# Generate a [0-9a-zA-Z] string
ALPHABET = map(str,range(0, 10)) + map(chr, range(97, 123) + range(65, 91))

def encode_id(id_number, alphabet=ALPHABET):
    """Convert an integer to a string."""
    if id_number == 0:
        return alphabet[0]

    alphabet_len = len(alphabet) # Cache

    result = ''
    while id_number > 0:
        id_number, mod = divmod(id_number, alphabet_len)
        result = alphabet[mod] + result

    return result

def decode_id(id_string, alphabet=ALPHABET):
    """Convert a string to an integer."""
    alphabet_len = len(alphabet) # Cache
    return sum([alphabet.index(char) * pow(alphabet_len, power) for power, char in enumerate(reversed(id_string))])

Це дуже швидко і може зайняти довгі цілі числа.


0

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


Чи розробив цей підхід для вас у довгостроковій перспективі?
Кріс

Якщо чесно, я не маю уявлення, на який проект я там мав на увазі :-P
Joel Berger

0

У мене є варіант проблеми в тому, що я зберігаю веб-сторінки від багатьох різних авторів і потрібно запобігати виявленню сторінок здогадками. Тому мої короткі URL-адреси додають пару додаткових цифр до рядка Base-62 для номера сторінки. Ці додаткові цифри генеруються з інформації в самій сторінці сторінки, і вони гарантують, що лише 1 з 3844 URL-адрес є дійсними (якщо вважати двозначною базою-62). Опис контуру ви можете побачити на веб- сайті http://mgscan.com/MBWL .


0

Дуже гарна відповідь, я створив реалізацію 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


0
/**
 * <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

Впровадження в 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)
  }

}

0

Функція, заснована в Xeoncross Class

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

0

Ось реалізація 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;
    };
}

Що ви маєте на увазі під "bit.ly." ?
Пітер Мортенсен

0

Моя версія 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")

0

Щоб отримати якісне рішення Node.js / JavaScript, дивіться модуль скорочення id , який ретельно перевірений та використовується у виробництві місяцями.

Він забезпечує ефективний скорочувач ідентифікаторів / 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

0

Чому б просто не створити випадковий рядок і додати його до базової 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);
}

Потім просто додайте додавання випадкової рядки до базовогоURL:

string tinyURL = baseUrl + RandomString(5);

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


0

Це мої початкові думки, і можна більше роздумувати, або зробити якесь моделювання, щоб зрозуміти, чи добре воно працює чи потрібне якесь вдосконалення:

Моя відповідь повинен пам'ятати довгий 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, так що тепер Сервер має 1000 таких ідентифікаторів. Тож якщо 20 або 200 серверів постійно хочуть нових ідентифікаторів, не потрібно постійно запитувати кожен новий ідентифікатор, а просити один раз на 1000 ідентифікаторів
  2. наприклад, для ID 1, наприклад, переверніть біти. Так 000...00000001стає 10000...000, так що при перетворенні на base64 він буде нерівномірно збільшувати ідентифікатори кожного разу.
  3. використовуйте XOR для гортання бітів для остаточних ідентифікаторів. Наприклад, XOR з 0xD5AA96...2373(як секретний ключ), і деякі біти будуть перевернуті. (щоразу, коли в секретному ключі буде включений 1 біт, він переверне біт ідентифікатора). Це зробить ідентифікатори ще складнішими в здогадуванні та виявляться більш випадковими

Дотримуючись цієї схеми, єдиний сервер, який виділяє ідентифікатори, може формувати ідентифікатори, і таким чином можуть 20 або 200 серверів, які вимагають виділення ідентифікаторів. Сервер, що розподіляє, повинен використовувати блокування / семафор, щоб запобігти двом серверам-запитам отримувати одну і ту ж пакет (або якщо він приймає одне з'єднання за один раз, це вже вирішує проблему). Тому ми не хочемо, щоб лінія (черга) була занадто довгою, щоб чекати, щоб отримати розподіл. Тож саме тому виділення 1000 або 10000 одночасно може вирішити проблему.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.