How do I create a URL shortener?

后端 未结 30 2020
我寻月下人不归
我寻月下人不归 2020-11-22 05:11

I want to create a URL shortener service where you can write a long URL into an input field and the service shortens the URL to \"http://www.example.org/abcdef\

相关标签:
30条回答
  • 2020-11-22 05:20

    Implementation in 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)
      }
    }
    

    Test example with 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 讨论(0)
  • 2020-11-22 05:22
    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 讨论(0)
  • 2020-11-22 05:23

    I would continue your "convert number to string" approach. However, you will realize that your proposed algorithm fails if your ID is a prime and greater than 52.

    Theoretical background

    You need a Bijective Function f. This is necessary so that you can find a inverse function g('abc') = 123 for your f(123) = 'abc' function. This means:

    • There must be no x1, x2 (with x1 ≠ x2) that will make f(x1) = f(x2),
    • and for every y you must be able to find an x so that f(x) = y.

    How to convert the ID to a shortened URL

    1. Think of an alphabet we want to use. In your case, that's [a-zA-Z0-9]. It contains 62 letters.
    2. Take an auto-generated, unique numerical key (the auto-incremented id of a MySQL table for example).

      For this example, I will use 12510 (125 with a base of 10).

    3. Now you have to convert 12510 to X62 (base 62).

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

      This requires the use of integer division and modulo. A pseudo-code example:

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

      Now map the indices 2 and 1 to your alphabet. This is how your mapping (with an array for example) could look like:

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

      With 2 → c and 1 → b, you will receive cb62 as the shortened URL.

      http://shor.ty/cb
      

    How to resolve a shortened URL to the initial ID

    The reverse is even easier. You just do a reverse lookup in your alphabet.

    1. e9a62 will be resolved to "4th, 61st, and 0th letter in the alphabet".

      e9a62 = [4,61,0] = 4×622 + 61×621 + 0×620 = 1915810

    2. Now find your database-record with WHERE id = 19158 and do the redirect.

    Example implementations (provided by commenters)

    • C++
    • Python
    • Ruby
    • Haskell
    • C#
    • CoffeeScript
    • Perl
    0 讨论(0)
  • 2020-11-22 05:23

    C# version:

    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;
        }   
    }
    
    0 讨论(0)
  • 2020-11-22 05:24
    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;
        }   
    }
    
    0 讨论(0)
  • 2020-11-22 05:24

    For a quality Node.js / JavaScript solution, see the id-shortener module, which is thoroughly tested and has been used in production for months.

    It provides an efficient id / URL shortener backed by pluggable storage defaulting to Redis, and you can even customize your short id character set and whether or not shortening is idempotent. This is an important distinction that not all URL shorteners take into account.

    In relation to other answers here, this module implements the Marcel Jackwerth's excellent accepted answer above.

    The core of the solution is provided by the following 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
    
    0 讨论(0)
提交回复
热议问题