Nasıl URL kısaltıcı oluşturabilirim?


667

Bir giriş alanına uzun bir URL yazabileceğiniz ve hizmet URL'yi " http://www.example.org/abcdef" olarak kısaltan bir URL kısaltıcı hizmeti oluşturmak istiyorum .

" abcdef" Yerine altı karakter içeren başka bir dize olabilir a-z, A-Z and 0-9. Bu 56 ~ 57 milyar olası dizgileri yapar.

Benim yaklaşımım:

Üç sütun içeren bir veritabanı tablo var:

  1. kimlik, tamsayı, otomatik artış
  2. uzun, dize, kullanıcının girdiği uzun URL
  3. kısa, dize, kısaltılmış URL (veya yalnızca altı karakter)

Daha sonra uzun URL'yi tabloya eklerdim. Sonra " id" için otomatik artış değerini seçer ve bir karma oluştururdum. Bu karma daha sonra " short" olarak eklenmelidir . Ama ne tür bir karma yapmalıyım? MD5 gibi karma algoritmalar çok uzun dizeler oluşturur. Bence bu algoritmaları kullanmıyorum. Kendi kendine oluşturulmuş bir algoritma da çalışacaktır.

Benim fikrim:

" http://www.google.de/" İçin otomatik artış kimliğini alıyorum 239472. Sonra aşağıdaki adımları gerçekleştiriyorum:

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.

Bu sayı bölünemez hale gelinceye kadar tekrarlanabilir. Bunun iyi bir yaklaşım olduğunu düşünüyor musunuz? Daha iyi bir fikrin var mı?

Bu konuya olan ilgiden dolayı, GitHub'a JavaScript , PHP , Python ve Java uygulamaları ile etkin bir çözüm yayınladım . İsterseniz çözümlerinizi ekleyin :)


5
@gudge Bu işlevlerin amacı, ters bir işleve sahip olmalarıdır. Bu, her ikisine de encode()ve decode()işlevlere sahip olabileceğiniz anlamına gelir . Adımlar nedenle şunlardır: (1) Kaydet URL veritabanında (2) ile kısa dizeye veritabanı (3) dönüştürme tamsayı kimliğinden bu URL için benzersiz satır kimliği alın encode(), örneğin 273984üzere f5a4(4) kısa bir dize (örn kullanın f4a4olarak) sizin paylaşılabilir URL'ler (5) Kısa bir dize (örn. 20a8) için bir istek alırken , dizeyi decode()(6) ile bir tamsayı kimliğine göre kodunu çözün . Dönüşüm için şunu kullanın: github.com/delight-im/ShortURL
caw

@Marco, karmayı veritabanında depolamanın anlamı nedir?
Maksim Vi.

3
@MaksimVi. Ters çevrilebilir bir fonksiyonunuz varsa, hiçbiri yoktur. Tek yönlü bir karma işleviniz olsaydı, bir tane olurdu.
caw

1
URL'yi kısaltmak için basit CRC32 algoritması kullanırsak yanlış olur mu? Çarpışma olasılığı çok düşük olmasına rağmen (CRC32 çıktısı genellikle 8 karakter uzunluğundadır ve bu bize 30 milyondan fazla olasılık verir) Daha önce oluşturulmuş bir CRC32 çıktısı daha önce kullanılmışsa ve veritabanında bulunursa, uzun URL'yi rastgele bir sayı ile tuzlayabiliriz Veritabanımda benzersiz bir CRC32 çıktısı bulana kadar. Bu basit bir çözüm için ne kadar kötü veya farklı veya çirkin olurdu?
Rakib

Yanıtlar:


816

"Numarayı dizeye dönüştür" yaklaşımınıza devam edeceğim. Ancak, kimliğiniz asal ve 52'den büyükse önerilen algoritmanızın başarısız olduğunu anlayacaksınız .

Teorik arka plan

İki Yönlü Bir İşlev Gerekiyor f . Bu, f (123) = 'abc' fonksiyonunuz için g ('abc') = 123 ters fonksiyonunu bulabilmeniz için gereklidir. Bunun anlamı:

  • F (x1) = f (x2) yapacak x1, x2 (x1 ≠ x2 ile) olmamalıdır ,
  • ve her için y bir bulmak mümkün olmalıdır x böylece f (x) = y .

Kimliği kısaltılmış bir URL'ye dönüştürme

  1. Kullanmak istediğimiz bir alfabeyi düşünün. Sizin durumunuzda, bu [a-zA-Z0-9]. 62 harf içerir .
  2. Otomatik olarak oluşturulan, benzersiz bir sayısal anahtarı alın ( idörneğin, bir MySQL tablosunun otomatik olarak artırılması ).

    Bu örnek için 125 10 ( 10 tabanlı 125) kullanacağım .

  3. Şimdi 125 10'u X 62'ye (temel 62) dönüştürmelisiniz.

    125 10 = 2 × 62 1 + 1 × 62 0 =[2,1]

    Bu, tamsayı bölme ve modulo kullanımını gerektirir. Bir sözde kod örneği:

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

    Şimdi indeks 2 ve 1'i alfabenize eşleyin. Eşlemeniz (örneğin bir dizi ile) şöyle görünebilir:

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

    2 → c ve 1 → b ile, kısaltılmış URL olarak cb 62 alırsınız .

    http://shor.ty/cb
    

Kısaltılmış URL ilk kimliğe nasıl çözülür?

Tersi daha da kolay. Sadece alfabenizde geriye doğru arama yaparsınız.

  1. e9a 62 "alfabedeki 4., 61. ve 0. harflere " çözümlenecektir.

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

  2. Şimdi ile veritabanı kaydını bulun WHERE id = 19158ve yönlendirmeyi yapın.

Örnek uygulamalar (yorumcular tarafından sağlanır)


18
Kötü amaçlı javascript kodu için URL'leri sterilize etmeyi unutmayın! Javascript'in bir URL'de base64 olabileceğini unutmayın, bu yüzden sadece 'javascript'i aramak yeterli değildir. J
Bjorn

3
Bir fonksiyonun tersi olabilmesi için iki yönlü (nesnel ve nesnel) olması gerekir.
Gumbo

57
Düşünce için yiyecek, url'ye iki karakterlik bir sağlama toplamı eklemek yararlı olabilir. Bu, sisteminizdeki tüm URL'lerin doğrudan yinelenmesini önler. F (checksum (id)% (62 ^ 2)) + f (id) = url_id
koblas

6
URL'leri dezenfekte ederken, karşılaşacağınız sorunlardan biri, spam filtrelerini önlemek için URL'lerinizi maskelemek için hizmetinizi kullanan spam göndericilerdir. Hizmeti bilinen iyi aktörlerle sınırlamanız veya uzun URL'lere spam filtrelemesi uygulamanız gerekir. Aksi takdirde spam gönderenler tarafından kötüye kullanılırsınız.
Edward Falk

74
Base62 kötü bir seçim olabilir, çünkü f * kelimeleri üretme potansiyeline sahiptir (örneğin, 3792586=='F_ck'_ yerine u ile). Bunu en aza indirmek için u / U gibi bazı karakterleri hariç tutarım.
Paulo Scardine

56

Neden bir karma kullanmak istersiniz?

Otomatik artış değerinizin alfasayısal bir değere basit bir çevirisini kullanabilirsiniz. Bazı temel dönüşümleri kullanarak bunu kolayca yapabilirsiniz. Diyelim ki karakter boşluğunda (AZ, az, 0-9 vb.) 40 karakter var, kimliği bir taban-40 numarasına dönüştürün ve karakterleri basamak olarak kullanın.


13
AZ, az ve 0-9 = 62 karakter değil, 40 değil, doğru işaret üzerindesiniz.
Evan Teran

Teşekkürler! O zaman baz-62 alfabesini kullanmalı mıyım? tr.wikipedia.org/wiki/Base_62 Ama kimlikleri nasıl bir baz-62 numarasına dönüştürebilirim?
16'da caw

Tabii bir temel dönüşüm algoritması kullanma - en.wikipedia.org/wiki/Base_conversion#Change_of_radix
06

2
"Neden bir karma kullanmak istesin?" sağ?
Andrew Coleson

2
yeterli kaynak ve zamanla, herhangi bir URL kısaltma hizmetinin tüm URL'lerine "göz atabilirsiniz".
21'de 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;
    }   
}

Fikri gerçekten seviyorum, bununla ilgili tek sorun, kod çözme işlevindeki num değişkenini sınırların dışına çıkarmaya devam etmem (hatta uzun süre), nasıl çalıştıracağına dair bir fikrin var mı? yoksa sadece teorik mi?
user1322801

@ user1322801: Muhtemelen kodlama işlevinin gerçekte işleyebileceğinden çok daha büyük bir şeyin kodunu çözmeye çalışıyorsunuz. Tüm "ints" ı BigInteger'e dönüştürdüyseniz, daha fazla kilometre elde edebilirsiniz, ancak> 9223372036854775807 dizinleriniz yoksa, muhtemelen yeterli olacaktır.
biggusjimmus

2
Tersine çevirmenin öneminin ne olduğunu öğrenebilir miyim? yani sb.reverse (). toString ();
dotNet Kod Çözücü

Bu 62 ^ 62 = 1.7 trilyon mu?
Noah Tony

33

Sorunuzun yanıtı değil, ancak büyük / küçük harfe duyarlı kısaltılmış URL'ler kullanmam. Hatırlanmaları zordur, genellikle okunamaz (birçok yazı tipi 1 ve l, 0 ve O ve diğer karakterleri çok yakındır, farkı söylemek imkansızdır) ve düpedüz hata eğilimli. Yalnızca küçük veya büyük harf kullanmayı deneyin.

Ayrıca, sayıları ve karakterleri önceden tanımlanmış bir biçimde karıştırdığınız bir biçime sahip olmaya çalışın. İnsanların bir formu diğerlerinden daha iyi hatırlama eğiliminde olduğunu gösteren çalışmalar vardır (sayıların belirli bir biçimde gruplandığı telefon numaralarını düşünün). Num-char-char-num-char-char gibi bir şey deneyin. Bunun kombinasyonları düşüreceğini biliyorum, özellikle büyük ve küçük harfiniz yoksa, ancak daha kullanışlı ve bu nedenle yararlı olacaktır.


2
Teşekkür ederim, çok iyi bir fikir. Bunu henüz düşünmedim. Bunun mantıklı olup olmadığının kullanım türüne bağlı olduğu açıktır.
09:22

19
İnsanlar kısa URL'leri kesinlikle kopyalayıp yapıştırıyorlarsa sorun olmaz.
Edward Falk

2
Kısa URL'lerin amacı unutulmaz veya konuşması kolay değildir. Sadece tıklayın veya kopyala / yapıştır.
Hugo Nogueira

Evet, kısa URL'nin yalnızca kullanıcıları listelemesi veya e-postayla göndermesi için olduğunu ve bu yüzden kısa olduğunu ve bazı URL'lerin yaptığı gibi 200 karakteri almayacağını
düşündüm

29

Yaklaşımım: Veritabanı Kimliğini al, sonra Base36 Kodla . Hem Büyük Hem de Küçük Harfleri KULLANMAYIM, çünkü bu, bu URL'leri telefonla bir kabus olarak iletir, ancak elbette işlevi bir temel 62 en / dekoder olarak kolayca genişletebilirsiniz.


Teşekkürler, haklısın. 2.176.782.336 olasılık veya 56.800.235.584'e sahip olun, aynı: Her ikisi de yeterli olacaktır. Bu yüzden base 36 kodlamasını kullanacağım.
caw


8

İşte benim PHP 5 sınıfım.

<?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

Bir Node.js ve MongoDB çözümü

MongoDB'nin 12 baytlık yeni bir ObjectId oluşturmak için kullandığı biçimi bildiğimiz için.

  • Unix döneminden bu yana geçen saniyeleri temsil eden 4 baytlık bir değer,
  • 3 baytlık makine tanımlayıcısı,
  • 2 baytlık bir işlem kimliği
  • rastgele bir değerle başlayan 3 baytlık bir sayaç (makinenizde).

Örnek (Rastgele bir dizi seçiyorum) a1b2c3d4e5f6g7h8i9j1k2l3

  • a1b2c3d4, Unix döneminden bu yana geçen saniyeleri temsil eder,
  • 4e5f6g7, makine tanımlayıcısını temsil eder,
  • h8i9 işlem kimliğini temsil eder
  • j1k2l3, rastgele bir değerle başlayan sayacı temsil eder.

Verileri aynı makinede saklıyorsak sayaç benzersiz olacağından, çoğaltılacağı şüphesi olmadan alabiliriz.

Kısa URL sayaç olacaktır ve işte sunucunuzun düzgün çalıştığını varsayan bir kod snippet'i.

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 / anahtar / değer çifti deposundan nasıl daha iyi olur?
kjs3

@ kjs3 evet haklısınız, çünkü diğer tablolarla hiçbir ilişki yok, RDBMS'ye gerek yok ve bir anahtar değer deposu daha hızlı olacak.
Firas Omrane

4

C # sürümü:

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

Ben veritabanında etki alanı başına bir tamsayı dizisi artan tutmak ve tamsayı bir URL yoluna kodlamak için Hashids kullanın.

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

Karakter uzunluğunu tüketene kadar ne kadar sürdüğünü görmek için bir senaryo çalıştırdım. Altı karakter için 164,916,224bağlantı yapabilir ve sonra yedi karaktere kadar çıkabilir. Bitly yedi karakter kullanır. Beş karakterin altında bana garip geliyor.

Hashids , URL yolunu bir tam sayıya dönüştürebilir, ancak daha basit bir çözüm, kısa bağlantının tamamını sho.rt/ka8ds3birincil anahtar olarak kullanmaktır.

İşte tam konsept:

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

İşte benim ihtiyaç duyduğum kişi için sürümüm.


1

Neden sadece kimliğinizi bir dizeye çevirmiyorsunuz? Sadece 0 ile 61 arasındaki bir rakamı tek bir harfle (büyük / küçük harf) veya rakamla eşleştiren bir fonksiyona ihtiyacınız vardır. Ardından, 4 harfli kodlar oluşturmak için bunu uygulayın ve 14,7 milyon URL'niz var.


Basit düşünce için +1. Gerçekten bu kadar basit. Tam olarak bunu yapan bir cevap gönderdim. Hiçbir yinelenen dizeleri ve her şey benzersiz olduğundan emin olmak için veritabanını sorgulayan bazı üretim kodu var.
Andrew Reese

1

İşte PHP için iyi bir URL kodlama fonksiyonu ...

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

Herkes bu yararlı bulabiliriz bilmiyorum - daha bir 'hack n slash' yöntemi, ama basit ve sadece belirli karakterleri istiyorsanız güzel çalışıyor.

$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

O, 0 ve i bilerek atladınız mı?

Ryan'ın çözümüne dayalı bir PHP sınıfı oluşturdum.

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

Evet. Sınıf bildiriminin hemen altındaki yorumu gördün mü?
Svetoslav Marinov


0

Ne kullanıyorum:

# 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))])

Çok hızlı ve uzun tamsayılar alabilir.


0

Benzer bir proje için, yeni bir anahtar almak için, benim hashtable zaten kullanılmamış bir dize elde edene kadar jeneratörü çağıran rastgele bir dize üreteci etrafında bir sarmalayıcı işlevi yapmak . Bu yöntem, ad alanınız dolmaya başladıktan sonra yavaşlar, ancak söylediğiniz gibi, sadece 6 karakterle bile, çalışmak için bol miktarda ad alanınız vardır.


Bu yaklaşım sizin için uzun vadede işe yaradı mı?
Chris

Dürüst olmak gerekirse, orada hangi projeye atıf yaptığımı bilmiyorum :-P
Joel Berger

0

Birçok farklı yazarın web sayfalarını sakladığım ve sayfaların tahmin yoluyla keşfedilmesini önlemem gereken bir problem varyantı var. Kısa URL'lerim, sayfa numarası için Base-62 dizesine birkaç ek basamak ekliyor. Bu fazladan basamaklar, sayfa kaydındaki bilgilerden üretilir ve 3844 URL'den yalnızca 1 tanesinin geçerli olmasını sağlar (2 basamaklı Base-62 olduğu varsayılarak). Http://mgscan.com/MBWL adresinde bir anahat açıklaması görebilirsiniz .


0

Çok iyi bir cevap, ben bjf bir Golang uygulaması yarattık:

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'da barındırılıyor: 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'da Uygulama:

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 testi ile test örneği:

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 Sınıfında fonksiyon

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

İşte bit.ly olması muhtemel bir Node.js uygulaması. oldukça rasgele yedi karakterli bir dize oluşturur.

Rastgele yedi karakter seçmek yerine oldukça rasgele 25 karakter kümesi oluşturmak için Node.js kripto kullanır.

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

Ne demek istiyorsun "bit.ly." ?
Peter Mortensen

0

Python 3 sürümüm

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

Kaliteli bir Node.js / JavaScript çözümü için, tamamen test edilmiş ve aylardır üretimde kullanılan id kısaltıcı modülüne bakın .

Redis'e varsayılan olarak takılabilir depolama alanı ile desteklenen etkili bir kimlik / URL kısaltıcı sağlar ve hatta kısa kimlik karakter kümenizi ve kısaltmanın idempotent olup olmadığını özelleştirebilirsiniz . Bu, tüm URL kısaltıcılarının dikkate almadığı önemli bir ayrımdır.

Buradaki diğer cevaplarla ilgili olarak, bu modül Marcel Jackwerth'ın yukarıdaki kabul edilmiş mükemmel cevabını uygular.

Çözümün çekirdeği aşağıdaki Redis Lua snippet'i tarafından sağlanır :

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

Neden sadece rastgele bir dize oluşturup onu temel URL'ye eklemiyorsunuz? Bu, C # ile yapmanın çok basitleştirilmiş bir sürümüdür .

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

Sonra rastgele dizeyi baseURL öğesine ekleyin:

string tinyURL = baseUrl + RandomString(5);

Bunun bunu yapmanın çok basitleştirilmiş bir sürümü olduğunu ve RandomString yönteminin yinelenen dizeler oluşturabileceğini unutmayın. Üretimde, her zaman benzersiz bir URL'nizin olmasını sağlamak için yinelenen dizeleri dikkate almak istersiniz. Herkes ilgileniyorsa paylaşabileceğiniz bir veritabanı tablosu sorgulayarak yinelenen dizeleri için hesap alır bazı kod var.


0

Bu benim ilk düşüncelerim ve daha fazla düşünme yapılabilir veya iyi çalışıp çalışmadığını veya herhangi bir iyileştirmenin gerekli olup olmadığını görmek için bazı simülasyonlar yapılabilir:

Benim cevabım veritabanında uzun URL'yi hatırlamak ve kimliğini kullanmaktır 0için 9999999999999999(veya ancak çok sayıda tabi).

Ancak 0 kimliği 9999999999999999bir sorun olabilir, çünkü

  1. onaltılı, hatta base62 veya base64 kullanırsak daha kısa olabilir. (base64 tıpkı YouTube gibi A- Z a- z 0- 9 _ve kullanıyor -)
  2. ondan artarsa 0için 9999999999999999düzgün bir gizlilik sorunu böylece, daha sonra korsanların, bu sırayla onları ziyaret ve URL'ler insanların birbirini gönderiyor bilemez

Bunu yapabiliriz:

  1. bir sunucunun bir sunucuya, A Sunucusuna tahsis 0edilmesini sağlayın 999, bu yüzden şimdi A Sunucusunda bu kimliklerin 1000'i vardır. Bu nedenle, sürekli olarak yeni kimlikler isteyen 20 veya 200 sunucu varsa, her yeni kimlik için sormaya devam etmek zorunda değil, 1000 kimlik için bir kez sormak zorunda kalır.
  2. örneğin ID 1 için bitleri tersine çevirin. Böylece 000...00000001olur 10000...000, böylece base64'e dönüştürüldüğünde, her seferinde eşit olmayan bir şekilde artan kimlikler olacaktır.
  3. son kimliklerin bitlerini çevirmek için XOR kullanın. Örneğin, XOR 0xD5AA96...2373(gizli bir anahtar gibi) ile bazı bitler ters çevrilir. (gizli anahtarın 1 biti açık olduğunda, kimliğin bitini çevirir). Bu, kimliklerin tahmin edilmesini daha da zorlaştıracak ve daha rastgele görünecektir.

Bu şemayı takiben, kimlikleri ayıran tek sunucu kimlikleri oluşturabilir ve kimlikleri tahsis etmeyi isteyen 20 veya 200 sunucuları da oluşturabilir. Ayırma sunucusu, istekte bulunan iki sunucunun aynı toplu işi almasını önlemek için bir kilit / semafor kullanmalıdır (veya bir seferde bir bağlantı kabul ediyorsa, bu zaten sorunu çözer). Bu nedenle, satırın (kuyruğun) ayırmayı beklemek için çok uzun olmasını istemiyoruz. Bu yüzden bir seferde 1000 veya 10000 ayırmak sorunu çözebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.