Làm cách nào để tạo một trình rút ngắn URL?


667

Tôi muốn tạo một dịch vụ rút ngắn URL, nơi bạn có thể viết một URL dài vào trường nhập và dịch vụ rút ngắn URL thành "http://www.example.org/abcdef ".

Thay vì " abcdef" có thể có bất kỳ chuỗi nào khác có sáu ký tự chứa a-z, A-Z and 0-9. Điều đó làm cho 56 ~ 57 tỷ chuỗi có thể.

Cách tiếp cận của tôi:

Tôi có một bảng cơ sở dữ liệu với ba cột:

  1. id, số nguyên, tự động tăng
  2. dài, chuỗi, URL dài người dùng đã nhập
  3. ngắn, chuỗi, URL rút ngắn (hoặc chỉ sáu ký tự)

Sau đó tôi sẽ chèn URL dài vào bảng. Sau đó, tôi sẽ chọn giá trị tăng tự động cho " id" và tạo giá trị băm của nó. Băm này sau đó nên được chèn là "short ". Nhưng tôi nên xây dựng loại băm nào? Các thuật toán băm như MD5 tạo ra các chuỗi quá dài. Tôi không sử dụng các thuật toán này, tôi nghĩ vậy. Một thuật toán tự xây dựng cũng sẽ làm việc.

Ý kiến ​​của tôi:

Đối với " http://www.google.de/" Tôi nhận được id tăng tự động 239472. Sau đó, tôi làm các bước sau:

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.

Điều đó có thể được lặp lại cho đến khi số không chia hết được nữa. Bạn có nghĩ rằng đây là một cách tiếp cận tốt? Bạn có ý kiển nào tốt hơn không?

Do sự quan tâm liên tục trong chủ đề này, tôi đã xuất bản một giải pháp hiệu quả cho GitHub , với các triển khai cho JavaScript , PHP , PythonJava . Thêm giải pháp của bạn nếu bạn thích :)


5
@gudge Điểm của các hàm đó là chúng có hàm nghịch đảo. Điều này có nghĩa là bạn có thể có cả hai encode()decode()chức năng. Các bước thực hiện, do đó: (1) Lưu URL trong cơ sở dữ liệu (2) Nhận ID hàng duy nhất cho rằng URL từ cơ sở dữ liệu (3) Chuyển đổi số nguyên ID để chuỗi ngắn với encode(), ví dụ như 273984để f5a4(4) Sử dụng các chuỗi ngắn (ví dụ f4a4) trong bạn URL có thể chia sẻ (5) Khi nhận được yêu cầu về một chuỗi ngắn (ví dụ 20a8), hãy giải mã chuỗi thành ID số nguyên với decode()(6) Tra cứu URL trong cơ sở dữ liệu cho ID đã cho. Để chuyển đổi, hãy sử dụng: github.com/delight-im/ShortURL
caw 10/2/2015

@Marco, điểm lưu trữ băm trong cơ sở dữ liệu là gì?
Maksim Vi.

3
@MaksimVi. Nếu bạn có một chức năng không thể đảo ngược, thì không có. Nếu bạn có hàm băm một chiều, sẽ có một hàm.
caw

1
Sẽ là sai nếu chúng ta sử dụng thuật toán CRC32 đơn giản để rút ngắn URL? Mặc dù rất khó xảy ra xung đột (đầu ra CRC32 thường dài 8 ký tự và cung cấp cho chúng tôi hơn 30 triệu khả năng) Nếu một đầu ra CRC32 được tạo đã được sử dụng trước đó và được tìm thấy trong cơ sở dữ liệu, chúng tôi có thể muối URL dài với một số ngẫu nhiên cho đến khi chúng tôi tìm thấy một đầu ra CRC32 là duy nhất trong cơ sở dữ liệu của tôi. Làm thế nào xấu hoặc khác biệt hoặc xấu xí này sẽ là một giải pháp đơn giản?
Rakib

Câu trả lời:


815

Tôi sẽ tiếp tục phương pháp "chuyển đổi số thành chuỗi" của bạn. Tuy nhiên, bạn sẽ nhận ra rằng thuật toán đề xuất của bạn thất bại nếu ID của bạn là số nguyên tố và lớn hơn 52 .

Cơ sở lý thuyết

Bạn cần một chức năng Bijective f . Điều này là cần thiết để bạn có thể tìm thấy hàm nghịch đảo g ('abc') = 123 cho hàm f (123) = 'abc' của bạn . Điều này có nghĩa là:

  • Không được có x1, x2 (với x1 x2) sẽ tạo ra f (x1) = f (x2) ,
  • và với mọi y bạn phải có thể tìm một x sao cho f (x) = y .

Cách chuyển đổi ID thành URL rút ngắn

  1. Hãy nghĩ về một bảng chữ cái chúng tôi muốn sử dụng. Trong trường hợp của bạn, đó là [a-zA-Z0-9]. Nó chứa 62 chữ cái .
  2. Lấy một khóa số duy nhất được tạo tự động ( idví dụ: tăng tự động của bảng MySQL).

    Trong ví dụ này, tôi sẽ sử dụng 125 10 (125 với cơ sở là 10).

  3. Bây giờ bạn phải chuyển đổi 125 10 sang X 62 (cơ sở 62).

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

    Điều này đòi hỏi sử dụng phép chia số nguyên và modulo. Một ví dụ mã giả:

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

    Bây giờ ánh xạ các chỉ số 2 và 1 vào bảng chữ cái của bạn. Đây là cách ánh xạ của bạn (ví dụ với một mảng) có thể trông như sau:

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

    Với 2 → c và 1 → b, bạn sẽ nhận được cb 62 dưới dạng URL rút ngắn.

    http://shor.ty/cb
    

Cách giải quyết một URL rút ngắn về ID ban đầu

Việc đảo ngược thậm chí còn dễ dàng hơn. Bạn chỉ cần làm một tra cứu ngược trong bảng chữ cái của bạn.

  1. e9a 62 sẽ được giải quyết thành "chữ cái thứ 4, 61 và 0 trong bảng chữ cái".

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

  2. Bây giờ hãy tìm bản ghi cơ sở dữ liệu của bạn với WHERE id = 19158và thực hiện chuyển hướng.

Ví dụ triển khai (được cung cấp bởi các bình luận viên)


18
Đừng quên vệ sinh các URL cho mã javascript độc hại! Hãy nhớ rằng javascript có thể được mã hóa base64 trong một URL nên chỉ cần tìm kiếm 'javascript' là không đủ.j
Bjorn

3
Một chức năng phải là tính từ (tiêm siêu thực) để có một nghịch đảo.
Gumbo

57
Thực phẩm cho suy nghĩ, nó có thể hữu ích để thêm một tổng kiểm tra hai ký tự vào url. Điều đó sẽ ngăn việc lặp lại trực tiếp tất cả các url trong hệ thống của bạn. Một cái gì đó đơn giản như f (checksum (id)% (62 ^ 2)) + f (id) = url_id
koblas

6
Theo như vệ sinh các url, một trong những vấn đề bạn sẽ gặp phải là những kẻ gửi thư rác sử dụng dịch vụ của bạn để che dấu URL của họ để tránh các bộ lọc thư rác. Bạn cần giới hạn dịch vụ đối với các tác nhân giỏi đã biết hoặc áp dụng lọc thư rác cho các url dài. Nếu không, bạn S be bị lạm dụng bởi những kẻ gửi thư rác.
Edward Falk

74
Base62 có thể là một lựa chọn tồi vì nó có khả năng tạo ra các từ f * (ví dụ: 3792586=='F_ck'với u ở vị trí _). Tôi sẽ loại trừ một số ký tự như u / U để giảm thiểu điều này.
Paulo Scardine

56

Tại sao bạn muốn sử dụng một hàm băm?

Bạn chỉ có thể sử dụng một bản dịch đơn giản của giá trị tăng tự động của bạn thành giá trị chữ và số. Bạn có thể làm điều đó một cách dễ dàng bằng cách sử dụng một số chuyển đổi cơ sở. Giả sử không gian ký tự của bạn (AZ, az, 0-9, v.v.) có 40 ký tự, chuyển đổi id thành số cơ sở 40 và sử dụng các ký tự làm chữ số.


13
ngoài thực tế là AZ, az và 0-9 = 62 ký tự, không phải 40, bạn đã đúng về nhãn hiệu.
Evan Teran

Cảm ơn! Tôi có nên sử dụng bảng chữ cái cơ sở 62 không? vi.wikipedia.org/wiki/Base_62 Nhưng làm cách nào tôi có thể chuyển đổi id thành số cơ sở 62?
caw

Sử dụng thuật toán chuyển đổi cơ sở ofcference - en.wikipedia.org/wiki/Base_conversion#Change_of_radix
shoosh

2
Về "Tại sao bạn muốn sử dụng hàm băm?", Một chuyển đổi cơ bản dựa trên mức tăng tự động sẽ tạo ra các URL liên tiếp, vì vậy bạn cần thoải mái với việc mọi người có thể "duyệt" các URL rút ngắn của người khác, đúng?
Andrew Coleson

2
có đủ tài nguyên và thời gian bạn có thể "duyệt" tất cả các URL của bất kỳ dịch vụ rút ngắn URL nào.
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;
    }   
}

Tôi thực sự thích ý tưởng này, vấn đề duy nhất tôi gặp phải là tôi cứ lấy biến num trong hàm giải mã ra khỏi giới hạn (thậm chí lâu dài), bạn có biết làm thế nào để nó hoạt động không? hay chỉ là lý thuyết?
user1322801

@ user1322801: Có lẽ bạn đang cố giải mã thứ gì đó lớn hơn nhiều so với chức năng mã hóa thực sự có thể xử lý. Bạn có thể nhận được thêm một số dặm nữa nếu bạn đã chuyển đổi tất cả "số nguyên" thành BigInteger, nhưng trừ khi bạn có các chỉ số> 9223372036854775807, có lẽ là đủ.
biggusjimmus

2
Tôi có thể biết tầm quan trọng của việc đảo ngược là gì? tức là sb.reverse (). toString ();
Bộ giải mã dotNet

Đó là 62 ^ 62 = 1,7 nghìn tỷ?

33

Không phải là một câu trả lời cho câu hỏi của bạn, nhưng tôi sẽ không sử dụng các URL rút ngắn phân biệt chữ hoa chữ thường. Chúng rất khó nhớ, thường không thể đọc được (nhiều phông chữ hiển thị 1 và l, 0 và O và các ký tự khác rất giống nhau đến mức chúng gần như không thể phân biệt được) và dễ bị lỗi. Cố gắng chỉ sử dụng chữ thường hoặc chữ hoa

Ngoài ra, hãy cố gắng có một định dạng nơi bạn trộn các số và ký tự ở dạng được xác định trước. Có những nghiên cứu cho thấy mọi người có xu hướng nhớ một hình thức tốt hơn những hình thức khác (nghĩ số điện thoại, trong đó các số được nhóm lại trong một hình thức cụ thể). Hãy thử một cái gì đó như num-char-char-num-char-char. Tôi biết điều này sẽ hạ thấp các kết hợp, đặc biệt nếu bạn không có chữ hoa và chữ thường, nhưng nó sẽ hữu dụng hơn và do đó hữu ích.


2
Cảm ơn bạn, ý tưởng rất tốt. Tôi chưa nghĩ về điều đó. Rõ ràng là nó phụ thuộc vào loại sử dụng cho dù điều đó có ý nghĩa hay không.
caw

19
Sẽ không thành vấn đề nếu mọi người nghiêm khắc sao chép và dán các url ngắn.
Edward Falk

2
Mục đích của url ngắn không phải là dễ nhớ hay dễ nói. Chỉ nhấp hoặc sao chép / dán.
Hugo Nogueira

vâng tôi nghĩ rằng URL ngắn chỉ dành cho mọi người để liệt kê hoặc gửi email và vì vậy nó ngắn và sẽ không chiếm 200 ký tự như một số URL, vì vậy trường hợp không phải là vấn đề
phân cực

29

Cách tiếp cận của tôi: Lấy ID cơ sở dữ liệu, sau đó mã hóa Base36 . Tôi sẽ KHÔNG sử dụng cả chữ in hoa và chữ thường, bởi vì điều đó làm cho việc truyền các URL đó qua điện thoại trở thành một cơn ác mộng, nhưng tất nhiên bạn có thể dễ dàng mở rộng chức năng để trở thành một bộ giải mã cơ sở 62.


Cảm ơn, bạn đã đúng. Cho dù bạn có 2.176.782.336 khả năng hay 56.800.235.584, thì đều như nhau: Cả hai đều là đủ. Vì vậy, tôi sẽ sử dụng mã hóa cơ sở 36.
caw

Có thể rõ ràng nhưng đây là một số mã PHP được tham chiếu trong wikipedia để thực hiện mã hóa base64 trong php tonymarston.net/php-mysql/converter.html
Ryan White

8

Đây là lớp 5 PHP của tôi.

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

Một giải pháp Node.js và MongoDB

Chỉnh sửa: Tốt hơn là sử dụng cơ sở dữ liệu quan hệ để lưu trữ dữ liệu đó (short_url và url thật) chứ không phải MongoDB.

Vì chúng ta biết định dạng mà MongoDB sử dụng để tạo ObjectId mới với 12 byte.

  • một giá trị 4 byte biểu thị các giây kể từ thời kỳ Unix,
  • một định danh máy 3 byte,
  • id quá trình 2 byte
  • bộ đếm 3 byte (trong máy của bạn), bắt đầu bằng một giá trị ngẫu nhiên.

Ví dụ (tôi chọn một chuỗi ngẫu nhiên) a1b2c3d4e5f6g7h8i9j1k2l3

  • a1b2c3d4 đại diện cho giây kể từ thời kỳ Unix,
  • 4e5f6g7 đại diện cho định danh máy,
  • h8i9 đại diện cho quá trình id
  • j1k2l3 đại diện cho bộ đếm, bắt đầu bằng một giá trị ngẫu nhiên.

Vì bộ đếm sẽ là duy nhất nếu chúng ta lưu trữ dữ liệu trong cùng một máy, chúng ta có thể lấy nó mà không nghi ngờ rằng nó sẽ bị trùng lặp.

Vì vậy, URL ngắn sẽ là bộ đếm và đây là đoạn mã giả định rằng máy chủ của bạn đang chạy đúng.

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

4

Phiên bản 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

Bạn có thể băm toàn bộ URL, nhưng nếu bạn chỉ muốn rút ngắn id, hãy làm như marcel đề xuất. Tôi đã viết triển khai Python này:

https://gist.github.com/778542


4

Tôi tiếp tục tăng một chuỗi số nguyên cho mỗi tên miền trong cơ sở dữ liệu và sử dụng Hashids để mã hóa số nguyên thành một đường dẫn URL.

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

Tôi đã chạy một kịch bản để xem mất bao lâu cho đến khi nó cạn kiệt chiều dài nhân vật. Đối với sáu nhân vật, nó có thể làm164,916,224 các liên kết và sau đó lên đến bảy ký tự. Bitly sử dụng bảy ký tự. Dưới năm nhân vật có vẻ kỳ lạ với tôi.

Hashids có thể giải mã đường dẫn URL trở lại một số nguyên nhưng một giải pháp đơn giản hơn là sử dụng toàn bộ liên kết ngắn sho.rt/ka8ds3làm khóa chính.

Đây là khái niệm đầy đủ:

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

Đây là phiên bản của tôi cho bất cứ ai cần nó.


1

Tại sao không chỉ dịch id của bạn thành một chuỗi? Bạn chỉ cần một hàm ánh xạ một chữ số giữa, giả sử 0 và 61 thành một chữ cái (chữ hoa / chữ thường) hoặc chữ số. Sau đó, áp dụng điều này để tạo, giả sử, mã gồm 4 chữ cái và bạn đã có 14,7 triệu URL được bảo hiểm.


+1 cho suy nghĩ đơn giản. Nó thật sự đơn giản. Tôi chỉ đăng một câu trả lời là làm chính xác điều này. Tôi có một số mã sản xuất truy vấn cơ sở dữ liệu để đảm bảo không có chuỗi trùng lặp và mọi thứ là duy nhất.
Andrew Reese

1

Đây là một chức năng mã hóa URL phong nha cho 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

Không biết liệu có ai thấy điều này hữu ích hay không - đó là phương pháp 'hack n slash', nhưng đơn giản và hoạt động độc đáo nếu bạn chỉ muốn các ký tự cụ thể.

$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

Bạn đã bỏ qua O, 0 và i về mục đích?

Tôi vừa tạo một lớp PHP dựa trên giải pháp của Ryan.

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

Đúng. Bạn có thấy bình luận ngay bên dưới lớp khai báo không?
Svetoslav Marinov

1

Hãy xem https://hashids.org/ nó là nguồn mở và bằng nhiều ngôn ngữ.

Trang của họ phác thảo một số cạm bẫy của các phương pháp khác.


0

Đây là những gì tôi sử dụng:

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

Nó rất nhanh và có thể mất số nguyên dài.


0

Đối với một dự án tương tự, để có được một khóa mới, tôi tạo một hàm bao quanh một trình tạo chuỗi ngẫu nhiên gọi trình tạo cho đến khi tôi nhận được một chuỗi chưa được sử dụng trong hàm băm của mình. Phương pháp này sẽ chậm lại khi không gian tên của bạn bắt đầu đầy, nhưng như bạn đã nói, thậm chí chỉ với 6 ký tự, bạn có rất nhiều không gian tên để làm việc.


Cách tiếp cận này có hiệu quả với bạn về lâu dài không?
Chris

Thành thật mà nói, tôi không biết tôi đang đề cập đến dự án nào :-P
Joel Berger

0

Tôi có một biến thể của vấn đề, trong đó tôi lưu trữ các trang web từ nhiều tác giả khác nhau và cần ngăn chặn việc phát hiện ra các trang bằng phỏng đoán. Vì vậy, các URL ngắn của tôi thêm một vài chữ số phụ vào chuỗi Base-62 cho số trang. Các chữ số phụ này được tạo từ thông tin trong bản ghi trang và chúng đảm bảo rằng chỉ 1 trong 3844 URL là hợp lệ (giả sử Base-62 gồm 2 chữ số). Bạn có thể xem mô tả phác thảo tại http://mgscan.com/MBWL .


0

Câu trả lời rất hay, tôi đã tạo ra một triển khai Golang của bjf:

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
}

Được lưu trữ tại 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

Thực hiện trong 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)
  }
}

Ví dụ kiểm tra với Scala test:

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

Chức năng dựa trên Lớp 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;
}

0

Đây là một triển khai Node.js có khả năng bit.ly. tạo ra một chuỗi bảy ký tự rất ngẫu nhiên.

Nó sử dụng tiền điện tử Node.js để tạo bộ ký tự 25 ngẫu nhiên cao thay vì chọn ngẫu nhiên bảy ký tự.

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

Bạn có ý nghĩa gì bởi "bit.ly." ?
Peter Mortensen

0

Phiên bản Python 3 của tôi

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

Để có giải pháp Node.js / JavaScript chất lượng, hãy xem mô-đun id-shortener , được kiểm tra kỹ lưỡng và đã được sử dụng trong sản xuất trong nhiều tháng.

Nó cung cấp một trình rút ngắn id / URL hiệu quả được hỗ trợ bởi mặc định lưu trữ có thể cắm vào Redis và thậm chí bạn có thể tùy chỉnh bộ ký tự id ngắn của mình và việc rút ngắn có phải là idempotent hay không . Đây là một điểm khác biệt quan trọng mà không phải tất cả các công cụ rút ngắn URL đều tính đến.

Liên quan đến các câu trả lời khác ở đây, mô-đun này triển khai câu trả lời được chấp nhận tuyệt vời của Marcel Jackwerth ở trên.

Cốt lõi của giải pháp được cung cấp bởi đoạn trích Redis Lua sau đây :

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

Tại sao không chỉ tạo một chuỗi ngẫu nhiên và nối nó vào URL cơ sở? Đây là một phiên bản rất đơn giản để làm điều này trong 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);
}

Sau đó, chỉ cần thêm chuỗi ngẫu nhiên vào cơ sởURL:

string tinyURL = baseUrl + RandomString(5);

Hãy nhớ rằng đây là một phiên bản rất đơn giản để thực hiện việc này và có thể phương thức RandomString có thể tạo các chuỗi trùng lặp. Trong sản xuất, bạn sẽ muốn tính đến các chuỗi trùng lặp để đảm bảo bạn sẽ luôn có một URL duy nhất. Tôi có một số mã tính đến các chuỗi trùng lặp bằng cách truy vấn bảng cơ sở dữ liệu mà tôi có thể chia sẻ nếu có ai quan tâm.


0

Đây là những suy nghĩ ban đầu của tôi, và có thể suy nghĩ nhiều hơn, hoặc một số mô phỏng có thể được thực hiện để xem liệu nó có hoạt động tốt hay cần cải thiện gì không:

Câu trả lời của tôi là ghi nhớ URL dài trong cơ sở dữ liệu và sử dụng ID 0để 9999999999999999(hoặc số lượng lớn là cần thiết).

Nhưng ID 0 9999999999999999có thể là một vấn đề, bởi vì

  1. nó có thể ngắn hơn nếu chúng ta sử dụng thập lục phân, hoặc thậm chí cơ sở62 hoặc cơ sở64. (base64 giống như YouTube sử dụng A- Z a- z 0- 9 _-)
  2. nếu nó tăng từ 0để 9999999999999999thống nhất, sau đó hacker có thể truy cập chúng theo thứ tự đó và biết những gì URL người đang gửi lẫn nhau, vì vậy nó có thể là một vấn đề riêng tư

Chung ta co thể lam được việc nay:

  1. có một máy chủ phân bổ 0cho999 một máy chủ, Máy chủ A, vì vậy hiện tại Máy chủ A có 1000 ID như vậy. Vì vậy, nếu có 20 hoặc 200 máy chủ liên tục muốn có ID mới, thì không cần phải yêu cầu mỗi ID mới, mà chỉ yêu cầu một lần cho 1000 ID
  2. ví dụ, đối với ID 1, đảo ngược các bit. Vì vậy, khi 000...00000001trở thành 10000...000Base64, nó sẽ được tăng ID không đồng đều mỗi lần.
  3. sử dụng XOR để lật các bit cho ID cuối cùng. Ví dụ, XOR với 0xD5AA96...2373(như một khóa bí mật) và một số bit sẽ được lật. (bất cứ khi nào khóa bí mật có 1 bit bật, nó sẽ lật bit của ID). Điều này sẽ làm cho ID thậm chí khó đoán hơn và xuất hiện ngẫu nhiên hơn

Theo sơ đồ này, máy chủ duy nhất phân bổ ID có thể tạo thành ID và 20 hoặc 200 máy chủ cũng có thể yêu cầu phân bổ ID. Máy chủ cấp phát phải sử dụng khóa / semaphore để ngăn hai máy chủ yêu cầu nhận cùng một đợt (hoặc nếu nó chấp nhận một kết nối tại một thời điểm, điều này đã giải quyết được vấn đề). Vì vậy, chúng tôi không muốn dòng (hàng đợi) quá dài để chờ phân bổ. Vì vậy, tại sao phân bổ 1000 hoặc 10000 tại một thời điểm có thể giải quyết vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.