如何创建URL缩短器?

时间:2009-04-12 16:29:16

标签: algorithm url

我想创建一个网址缩短服务,您可以在其中将长网址写入输入字段,该服务会将网址缩短为“http://www.example.org/abcdef”。

除了“abcdef”之外,还可以有包含a-z, A-Z and 0-9的六个字符的任何其他字符串。这使得56到570亿个可能的字符串。

我的方法:

我有一个包含三列的数据库表:

  1. id,integer,auto-increment
  2. long,string,用户输入的长网址
  3. 短,字符串,缩短的网址(或只是六个字符)
  4. 然后我会将长URL插入表中。然后我会选择“id”的自动增量值并构建它的哈希值。然后应该将此哈希插入为“short”。但是我应该构建什么样的哈希?像MD5这样的散列算法会创建太长的字符串。我想,我不使用这些算法。自建算法也可以。

    我的想法:

    对于“http://www.google.de/”,我获得了自动增量ID 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.
    

    这可以重复,直到数字不再可分。你认为这是一个好方法吗?你有更好的主意吗?

      

    由于对此主题的持续兴趣,我published an efficient solution to GitHub已实现JavaScriptPHPPython和{{ 3}}。如果您愿意,请添加您的解决方案:)

31 个答案:

答案 0 :(得分:767)

我会继续你的“转换数字到字符串”的方法。但是,如果您的ID是 prime且大于52 ,您将意识到您提出的算法会失败。

理论背景

您需要Bijective Function f 。这是必要的,以便您可以为 f(123)='abc'函数找到反函数 g('abc')= 123 。这意味着:

  • 必须没有 x1,x2(x1≠x2)会使 f(x1)= f(x2)
  • 并且对于每个 y ,您必须能够找到 x ,以便 f(x)= y

如何将ID转换为缩短的URL

  1. 想想我们想要使用的字母表。在你的情况下,那是[a-zA-Z0-9]。它包含 62个字母
  2. 使用自动生成的唯一数字键(例如MySQL表的自动递增id)。

    对于这个例子,我将使用125 10 (125基础为10)。

  3. 现在你必须将125 10 转换为X 62 (base 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
    
  4. 如何将缩短的URL解析为初始ID

    反过来更容易。你只需在字母表中进行反向查找。

    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的数据库记录并执行重定向。

    3. 一些实现(由评论者提供)

答案 1 :(得分:53)

为什么要使用哈希?

您可以使用自动增量值的简单转换为字母数字值。您可以使用一些基本转换轻松完成此操作。假设您的字符空间(A-Z,a-z,0-9等)有40个字符,将id转换为base-40数字并将字符用作数字。

答案 2 :(得分:47)

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

答案 3 :(得分:32)

不是您问题的答案,但我不会使用区分大小写的缩短网址。它们很难记住,通常是不可读的(许多字体渲染1和l,0和O以及其他字符非常相似,几乎不可能分辨出来)并且容易出错。尽量只使用小写或大写。

此外,尝试使用一种格式,以预定义的形式混合数字和字符。有研究表明,人们倾向于比其他人更好地记住一种形式(想想电话号码,其中数字以特定形式分组)。尝试使用num-char-char-num-char-char之类的东西。我知道这会降低组合,特别是如果你没有大写和小写,但它会更有用,因此更有用。

答案 4 :(得分:27)

我的方法:获取数据库ID,然后Base36 Encode it。我不会同时使用大写和小写字母,因为这会使通过电话传输这些URL成为一场噩梦,但您当然可以轻松地将该功能扩展为基本的62 en / decoder。

答案 5 :(得分: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 :(得分:5)

Node.js和MongoDB解决方案

因为我们知道MongoDB用来创建一个12字节的新ObjectId的格式。

  • 一个4字节的值,表示自Unix纪元以来的秒数,
  • 一个3字节的机器标识符,
  • 一个2字节的进程ID
  • 一个3字节的计数器(在您的机器中),以随机值开始。

示例(我选择随机序列) 的 a1b2c3d4e5f6g7h8i9j1k2l3

  • a1b2c3d4代表自Unix时代以来的秒数,
  • 4e5f6g7代表机器标识符,
  • h8i9表示进程ID
  • j1k2l3表示计数器,以随机值开始。

由于我们将数据存储在同一台机器中,因此计数器将是唯一的,我们可以毫不怀疑地将它复制。

因此,短网址将是计数器 ,这是一个代码段,假设您的服务器正常运行。

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

答案 7 :(得分: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;
    }   
}

答案 8 :(得分:4)

您可以对整个网址进行哈希处理,但如果您只想缩短ID,请按照马塞尔的建议进行操作。我写了这个Python实现:

https://gist.github.com/778542

答案 9 :(得分:3)

// simple approach

$original_id = 56789;

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

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

答案 10 :(得分:3)

看看https://hashids.org/,它是开源的,并且支持多种语言。

他们的页面概述了其他方法的一些陷阱。

答案 11 :(得分: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

这是我的版本,适合任何需要它的人。

答案 12 :(得分:2)

我不断在数据库中为每个域增加一个整数序列,并使用Hashids将整数编码为URL路径。

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

我运行了一个脚本,看看它耗尽了多长时间才能耗尽字符长度。对于六个字符,它可以执行164,916,224个链接,然后最多可以输出七个字符。有点使用七个字符。五个字符对我来说很奇怪。

Hashids可以将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)
}

答案 13 :(得分:2)

如果您不想重新发明轮子...... http://lilurl.sourceforge.net/

答案 14 :(得分:1)

这是一个适合PHP的URL编码功能......

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

答案 15 :(得分:1)

不知道是否有人会发现这有用 - 它更像是一种'黑客n斜线'方法,但如果你只想要特定的字符,它很简单并且效果很好。

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

答案 16 :(得分:1)

你是否故意省略O,0和我?

我刚刚根据Ryan的解决方案创建了一个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;
        }
    }
?>

答案 17 :(得分:0)

这是我最初的想法,可以做更多的思考,或者可以进行一些模拟以查看它是否运行良好或需要任何改进:

我的答案是记住数据库中的长URL,并使用ID 09999999999999999(或需要很大的数字)。

但是ID为0的9999999999999999可能是一个问题,因为

  1. 如果我们使用十六进制,甚至使用base62或base64,它可以更短。 (base64就像YouTube使用A-Z a-z 0-9 _-
  2. 如果它从09999999999999999统一增加,则黑客可以按此顺序访问它们,并知道人们彼此发送的URL,因此这可能是隐私问题

我们可以这样做:

  1. 有一台服务器将0分配给999到一台服务器,即服务器A,因此现在服务器A具有1000个这样的ID。因此,如果有20或200台服务器不断需要新的ID,则不必继续要求每个新ID,而只需一次询问1000个ID
  2. 例如,对于ID 1,
  3. 反转位。因此,000...00000001变成了10000...000,因此当转换为base64时,每次的ID都会不一致。
  4. 使用XOR翻转最终ID的位。例如,将0xD5AA96...2373与XOR(例如一个秘密密钥)进行XOR,则某些位将被翻转。 (只要密钥的1位打开,它将翻转ID的位)。这将使ID更加难以猜测,并且显得更加随机

按照此方案,分配ID的单个服务器可以形成ID,而请求分配ID的20或200个服务器也可以形成ID。分配服务器必须使用锁/信号量,以防止两个请求服务器获得相同的批处理(或者如果它一次接受一个连接,则已经解决了问题)。因此,我们不想等待等待分配的行(队列)太长。这就是为什么一次分配1000或10000可以解决此问题的原因。

答案 18 :(得分:0)

这是我在Google Cloud控制台中创建并部署的一个。 它是用Java和Spring Boot编写的。

它是https://jol.ink

如果您想获取详细信息,请在评论部分让我知道,我将编辑此帖子并进行详细说明

答案 19 :(得分: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);
}

然后只需将随机字符串添加到baseURL:

string tinyURL = baseUrl + RandomString(5);

请记住,这是一个非常简化的版本,RandomString方法可能会创建重复的字符串。在生产中,您需要考虑重复的字符串以确保您始终具有唯一的URL。我有一些代码通过查询数据库表来考虑重复的字符串,如果有人感兴趣,我可以共享该数据库表。

答案 20 :(得分:0)

Function based in 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;
}

答案 21 :(得分: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)
  }

}

答案 22 :(得分:0)

对于高质量的Node.js / JavaScript解决方案,请参阅id-shortener模块,该模块经过全面测试,已在生产中使用了数月。

它提供了一个有效的ID / URL缩短器,由可插入存储默认为 Redis ,您甚至可以自定义短ID字符集以及缩短幂等 。这是一个重要的区别,并非所有URL缩短程序都考虑在内。

关于其他答案,本模块实现了Marcel Jackwerth上面提到的优秀答案。

解决方案的核心由以下Redis Lua snippet提供:

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

答案 23 :(得分:0)

这是一个很可能是bit.ly的Node.js实现。生成一个高度随机的七个字符的字符串。

它使用Node.js加密来生成高度随机的25个字符集,而不是随机选择7个字符。

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

答案 24 :(得分: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")

答案 25 :(得分: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));
    }
}

答案 26 :(得分: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

答案 27 :(得分:0)

我有一个问题的变体,因为我存储了来自许多不同作者的网页,需要通过猜测来防止发现页面。所以我的短网址为Base-62字符串添加了几个额外的数字作为页码。这些额外数字是根据页面记录本身中的信息生成的,它们确保3844个URL中只有1个有效(假设2位Base-62)。您可以在http://mgscan.com/MBWL看到大纲说明。

答案 28 :(得分:0)

对于一个类似的项目,为了得到一个新的密钥,我在一个调用生成器的random string generator周围创建一个包装函数,直到我得到一个尚未在我的哈希表中使用的字符串。一旦你的名字空间开始变满,这个方法会变慢,但正如你所说的,即使只有6个字符,你也有足够的命名空间可供使用。

答案 29 :(得分: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))])

它非常快,可能需要很长的整数。

答案 30 :(得分:0)

为什么不将您的ID翻译成字符串?您只需要一个将数字(例如0到61)之间的数字映射到单个字母(大写/小写)或数字的函数。然后将其应用于创建4个字母的代码,并且覆盖了1470万个URL。