URL短縮サービスを作成するにはどうすればよいですか?


667

入力フィールドに長いURLを書き込むことができ、サービスがURLを「http://www.example.org/abcdef」に短縮するURL短縮サービスを作成したいと考えています。

abcdef」の代わりに、を含む6文字の文字列がありa-z, A-Z and 0-9ます。これにより、560〜570億の文字列が可能になります。

私のアプローチ:

3つの列を持つデータベーステーブルがあります。

  1. id、整数、自動インクリメント
  2. long、string、ユーザーが入力した長いURL
  3. 短い、文字列、短縮されたURL(または6文字のみ)

次に、長い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.

これは、数値が割り切れなくなるまで繰り返すことができます。これは良いアプローチだと思いますか?もっと良いアイデアはありますか?

このトピックへの関心が継続しているため、JavaScriptPHPPythonJavaの実装を使用してGitHubの効率的なソリューションを公開しました。必要に応じてソリューションを追加してください:)


5
@gudgeそれらの関数のポイントは、それらが逆関数を持っていることです。つまり、encode()との両方のdecode()機能を持つことができます。手順は、それゆえ、以下のとおりです。データベース内で(1)保存URL(2)データベース(3)からそのURLの一意の行IDを取得ID整数変換短い文字列にしてencode()、例えば273984f5a4使用(4)短い文字列(例えばf4a4)あなたに共有可能なURL(5)短い文字列(例:)のリクエストを受け取った場合20a8、文字列を整数IDにデコードしますdecode()(6)データベースでURLを検索して指定されたIDを探します。変換のため、使用:github.com/delight-im/ShortURL
CAW

@マルコ、ハッシュをデータベースに保存する意味は何ですか?
Maksim Vi。

3
@MaksimVi。あなたが可逆関数を持っている場合、何もありません。一方向のハッシュ関数がある場合は、1つあります。
2015

1
単純なCRC32アルゴリズムを使用してURLを短くすると、間違いでしょうか?衝突の可能性は非常に低いですが(CRC32出力は通常8文字で、3,000万を超える可能性があります)生成されたCRC32出力がすでに使用されており、データベースで見つかった場合、長いURLに乱数をソルトできますデータベースで一意のCRC32出力が見つかるまで。これは単純なソリューションにとってどれほど悪いか、異なるか、醜いですか?
Rakib 2016年

回答:


815

「数値を文字列に変換する」アプローチを続けます。ただし、IDが素数で52より大きい場合、提案されたアルゴリズムが失敗することに気付くでしょう。

理論的背景

全単射関数 fが必要です。これは、f(123)= 'abc'関数の逆関数g( 'abc')= 123を見つけるために必要です。これの意味は:

  • f(x1)= f(x2)となるx1、x2(x1≠x2)があってはなりません。
  • そしてすべてのyについて、f(x)= yとなるようにxを見つけることができなければなりません。

IDを短縮URLに変換する方法

  1. 使用したいアルファベットを考えてください。あなたの場合、それは[a-zA-Z0-9]です。これは、含まれている62個の文字を
  2. 自動生成された一意の数値キー(idたとえば、MySQLテーブルの自動インクリメント)を取ります。

    この例では、125 10(底が10の125)を使用します。

  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の場合、短縮URLとしてcb 62を受け取ります。

    http://shor.ty/cb
    

短縮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リダイレクトします。

実装例(コメント提供者が提供)


18
悪意のあるJavaScriptコードのURLをサニタイズすることを忘れないでください!これだけのJavaScript "を検索すると良いenough.jないというJavascriptをURLにbase64エンコードすることができ覚えておいてください
ビョルン・

3
逆関数を持つ関数は、全単射(単射および全射)でなければなりません。
ガンボ

57
考えてみてください。URLに2文字のチェックサムを追加すると便利です。これにより、システム内のすべてのURLが直接反復されるのを防ぐことができます。f(checksum(id)%(62 ^ 2))+ f(id)= url_idのような単純なもの
koblas

6
URLのサニタイズに関する限り、直面する問題の1つは、スパマーがサービスを使用してURLをマスクし、スパムフィルターを回避することです。サービスを既知の優れたアクターに制限するか、長いURLにスパムフィルタリングを適用する必要があります。そうでなければ、あなたはスパマーに虐待されます。
エドワードフォーク

74
Base62は、f *ワードを生成する可能性があるため(たとえば、3792586=='F_ck'_の代わりにuを使用する)、不適切な選択になる場合があります。これを最小限に抑えるために、u / Uなどの一部の文字を除外します。
Paulo Scardine 2013年

56

なぜハッシュを使いたいのですか?

自動インクリメント値を英数字値に単純に変換するだけで使用できます。いくつかの基本変換を使用することで、それを簡単に行うことができます。文字スペース(AZ、az、0-9など)が40文字で、idをbase-40数に変換し、その文字を数字として使用するとします。


13
AZ、azおよび0-9 = 62文字であり、40ではないという事実から、あなたは正解です。
エヴァンテラン

ありがとう!次に、base-62アルファベットを使用する必要がありますか? en.wikipedia.org/wiki/Base_62 しかし、IDをbase-62の数値に変換するにはどうすればよいですか?
2009

-勿論基数変換アルゴリズムを用いen.wikipedia.org/wiki/Base_conversion#Change_of_radix
shoosh

2
「なぜハッシュを使用したいのですか?」については、自動インクリメントに基づく基本変換で順次URLが作成されるため、他の人の短縮URLを「閲覧」できることに慣れる必要があります。正しい?
Andrew Coleson

2
十分なリソースと時間があれば、URL短縮サービスのすべてのURLを「参照」できます。
2009

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変数を境界から外に出し続けることです(たとえ長い場合でも)、それを機能させる方法について何か考えがありますか?それとも理論的なだけですか?
user1322801

@ user1322801:おそらく、エンコード関数が実際に処理できるものよりもはるかに大きなものをデコードしようとしていると考えられます。すべての「int」をBigIntegerに変換した場合、さらに多くのマイレージを得ることができますが、9223372036854775807を超えるインデックスがない限り、おそらく十分な長さになります。
biggusjimmus

2
リバースの重要性は何ですか?つまりsb.reverse()。toString();
dotNet Decoder 2017

62 ^ 62 = 1.7兆ですか?
ノアトニー

33

質問への回答ではありませんが、大文字と小文字を区別する短縮URLは使用しません。それらを覚えるのは難しく、通常は判読できません(多くのフォントは1とl、0とOをレンダリングし、他の文字は非常によく似ているため、違いを見分けるのはほとんど不可能です)、間違いなくエラーが発生しやすくなります。小文字または大文字のみを使用してください。

また、数値と文字を事前に定義された形式で混在させるフォーマットを用意してください。人々はある形式を他の形式よりよく覚えている傾向があることを示す研究があります(番号が特定の形式にグループ化されている電話番号を考えてください)。num-char-char-num-char-charのようなものを試してください。これにより、特に大文字と小文字がない場合は組み合わせが低くなることはわかっていますが、より使いやすく、したがって便利です。


2
ありがとう、とてもいい考え。まだ考えていません。それが意味があるかどうかは、使用の種類に依存することは明らかです。
2009

19
短いURLを厳密にコピーアンドペーストする場合は問題ありません。
エドワードフォーク2013年

2
短いURLの目的は、記憶に残るものや話しやすいものにすることではありません。クリックまたはコピー/貼り付けのみです。
Hugo Nogueira

ええ、私は場合は問題ではありませんので、短いURLが、人々はそれをリストしたり、それを電子メールで送信するだけのためであり、それは短いですし、いくつかのURLなどの200個の文字がない取らないようにと思った
nonopolarity

29

私のアプローチ:データベースIDを取得し、次にBase36でエンコードします。大文字と小文字の両方を使用することはありません。電話を介してこれらのURLを送信することは悪夢のようになるためですが、もちろん、関数をbase 62 en / decoderに拡張することも簡単にできます。


ありがとう、あなたは正しい。2,176,782,336の可能性がある場合でも、56,800,235,584の可能性がある場合でも、同じです。両方で十分です。したがって、ベース36エンコーディングを使用します。
2009

明らかかもしれませんが、php tonymarston.net/php-mysql/converter.htmlで
Ryan White

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ではなく、リレーショナルデータベースを使用して、そのようなデータ(short_urlおよびtrue url)を保存することをお勧めします。

MongoDBが12バイトの新しいObjectIdを作成するために使用する形式を知っているからです。

  • Unixエポックからの秒数を表す4バイトの値
  • 3バイトのマシンID
  • 2バイトのプロセスID
  • ランダムな値で始まる3バイトのカウンター(マシン内)。

例(ランダムなシーケンスを選択) a1b2c3d4e5f6g7h8i9j1k2l3

  • a1b2c3d4はUnixエポックからの秒数を表し、
  • 4e5f6g7はマシン識別子を表し、
  • h8i9はプロセスIDを表します
  • 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");
    })
});

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

データベース内のドメインごとに整数シーケンスを増分し続け、Hashidsを使用して整数をURLパスにエンコードします。

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

スクリプトを実行して、文字長がなくなるまでにかかる時間を確認しました。6文字の場合は164,916,224リンクを作成でき、最大7文字まで追加できます。Bitlyは7文字を使用します。5文字未満は私には奇妙に見えます。

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


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

IDを文字列に変換しないのはなぜですか?たとえば、0から61までの数字を1文字(大文字/小文字)または数字にマップする関数が必要です。次に、これを適用して、たとえば4文字のコードを作成すると、1470万のURLがカバーされます。


+1は単純な考え方です。とても簡単です。私はまさにこれをしている答えを投稿しました。データベースにクエリを実行して、重複する文字列がなく、すべてが一意であることを確認するいくつかの本番コードがあります。
Andrew Reese、

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

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

意図的にO、0、iを省略しましたか?

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

はい。クラス宣言のすぐ下にコメントがありましたか?
Svetoslav Marinov 2015年

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文字しかなくても、使用できるネームスペースがたくさんあります。


このアプローチは長い目で見ればうまくいっていますか?
Chris

正直言って、私はそこでどのプロジェクトを参照していたのか分かりません:-P
Joel Berger

0

私にはさまざまな問題があります。私は多くの異なる作者からのWebページを保存しており、当て推量によるページの発見を防ぐ必要があるからです。そのため、私の短いURLでは、ページ番号のBase-62文字列にいくつかの数字が追加されています。これらの追加の数字は、ページレコード自体の情報から生成され、3844のURLのうち1つだけが有効であることを保証します(2桁のBase-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クラスに基づく関数

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

以下は、bit.lyである可能性が高いNode.js実装です。非常にランダムな7文字の文字列を生成します。

Node.js暗号を使用して、ランダムに7文字を選択するのではなく、非常にランダムな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-shortenerモジュールをご覧ください。

それはをデフォルトプラグイン可能なストレージに裏打ちされた、効率的なID / URL短縮サービスを提供Redisのを、あなたもあなたの短いID文字セットをカスタマイズすることができますし、かどうかを短縮であるべき等。これは、すべてのURL短縮機能が考慮されるわけではない重要な違いです。

ここでの他の回答との関連で、このモジュールは上記のMarcel Jackwerthの優れた受け入れられた回答を実装します。

ソリューションの中核は、次の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);
}

次に、ランダムな文字列をbaseURLに追加します。

string tinyURL = baseUrl + RandomString(5);

これはこれを非常に単純化したバージョンであり、RandomStringメソッドが重複した文字列を作成する可能性があることを覚えておいてください。本番環境では、重複する文字列を考慮に入れて、常に一意のURLを確保する必要があります。誰かが興味を持っている場合に共有できるデータベーステーブルにクエリを実行して、重複する文字列を考慮するコードをいくつか持っています。


0

これは私の最初の考えであり、より多くの考えを行うことができます。または、いくつかのシミュレーションを行って、それがうまく機能するか、または改善が必要かどうかを確認できます。

私の答えは、データベース内の長いURLを記憶し、ID 0を使用することです9999999999999999(ただし、必要な数は大きいです)。

ただし、ID 0 9999999999999999は問題になる可能性があります。

  1. 16進数、またはbase62またはbase64を使用する場合は、短くなる可能性があります。(ちょうどYouTubeのようなbase64で使用するA- Z a- z 0- 9 _-
  2. から09999999999999999均一に増加した場合、ハッカーはその順序で訪問し、人々が互いに送信しているURLを知ることができるため、プライバシーの問題になる可能性があります。

できるよ:

  1. 1台のサーバーを1台のサーバー(サーバーA)に割り当て0ている999ため、サーバーAにはそのようなIDが1000個あります。したがって、常に新しいIDを必要とするサーバーが20または200ある場合、新しいIDごとに要求し続ける必要はなく、1000のIDを1回要求する必要があります。
  2. たとえば、ID 1の場合、ビットを逆にします。したがって、に 000...00000001なる10000...000ため、base64に変換すると、毎回IDが不均一に増加します。
  3. XORを使用して、最終IDのビットを反転します。たとえば、0xD5AA96...2373(秘密鍵のような)XORを使用すると、一部のビットが反転されます。(秘密鍵のビットが1になっていると、IDのビットが反転します)。これにより、IDの推測がさらに難しくなり、よりランダムに表示されます

このスキームに従って、IDを割り当てる単一のサーバーがIDを形成し、IDの割り当てを要求する20台または200台のサーバーもIDを形成できます。割り当てサーバーは、ロック/セマフォを使用して、2つの要求サーバーが同じバッチを取得しないようにする必要があります(または、一度に1つの接続を受け入れる場合、これで問題が解決します)。したがって、行(キュー)が割り当てを取得するために待機するのに長すぎないようにします。そのため、一度に1000または10000を割り当てることで問題を解決できるのはこのためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.