[轉]微博URL短網址生成算法原理及(java版、php版實現實例)
短網址(Short URL),顧名思義就是在形式上比較短的網址。通常用的是asp或者php轉向,在Web 2.0的今天,不得不說,這是一個潮流。目前已經有許多類似服務,借助短網址您可以用簡短的網址替代原來冗長的網址,讓使用者可以更容易的分享鏈接。
例如:http://t.cn/SzjPjA
短網址服務,可能很多朋友都已經不再陌生,現在大部分微博、手機郵件提醒等地方已經有很多應用模式了,並占據了一定的市場。估計很多朋友現在也正在使用。
看過新浪的短連接服務,發現后面主要有6個字符串組成,於是第一個想到的就是原來公司寫的一個游戲激活碼規則,也就是下面的算法2,
26個大寫字母 26小寫字母,10個數字,隨機生成6個然后插入數據庫對應一個id,短連接跳轉的時候,根據字符串查詢到對應id,即可實現相應的跳轉!不過2的62次方,不知道有沒有重復的,小概率可以,但是對應不是很大的網站應該足夠了
自從twitter推出短網址(shorturl),繼之國內各大微博跟風,google公開goo.gl使用API,短網址之風愈演愈烈.不得不說這是一個新興又一大熱門web2.0服務.現整理一下,包括完整短網址網站,短網址生成原理,算法舉例,以及優劣比較,同時還介紹幾個phper個人實現的。
短鏈接的好處:
1、內容需要;2、用戶友好;3、便於管理。
為什么要這樣做的,原因我想有這樣幾點:
- 微博限制字數為140字一條,那么如果我們需要發一些連接上去,但是這個連接非常的長,以至於將近要占用我們內容的一半篇幅,這肯定是不能被允許的,所以短網址應運而生了。
- 短網址可以在我們項目里可以很好的對開放級URL進行管理。有一部分網址可以會涵蓋暴力,廣告等信息,這樣我們可以通過用戶的舉報,完全管理這個連接將不出現在我們的應用中,應為同樣的URL通過加密算法之后,得到的地址是一樣的。
- 我們可以對一系列的網址進行流量,點擊等統計,挖掘出大多數用戶的關注點,這樣有利於我們對項目的后續工作更好的作出決策。
算法原理
算法一
1)將長網址md5生成32位簽名串,分為4段, 每段8個字節;
2)對這四段循環處理, 取8個字節, 將他看成16進制串與0x3fffffff(30位1)與操作, 即超過30位的忽略處理;
3)這30位分成6段, 每5位的數字作為字母表的索引取得特定字符, 依次進行獲得6位字符串;
4)總的md5串可以獲得4個6位串; 取里面的任意一個就可作為這個長url的短url地址;
這種算法,雖然會生成4個,但是仍然存在重復幾率,下面的算法一和三,就是這種的實現.
算法二
a-zA-Z0-9 這64位取6位組合,可產生500多億個組合數量.把數字和字符組合做一定的映射,就可以產生唯一的字符串,如第62個組合就是aaaaa9,第63個組合就是aaaaba,再利用洗牌算法,把原字符串打亂后保存,那么對應位置的組合字符串就會是無序的組合。
把長網址存入數據庫,取返回的id,找出對應的字符串,例如返回ID為1,那么對應上面的字符串組合就是bbb,同理 ID為2時,字符串組合為bba,依次類推,直至到達64種組合后才會出現重復的可能,所以如果用上面的62個字符,任意取6個字符組合成字符串的話,你的數據存量達到500多億后才會出現重復的可能。
具體參看這里徹底完善新浪微博接口和超短URL算法,算法四可以算作是此算法的一種實現,此算法一般不會重復,但是如果是統計的話,就有很大問題,特別是對域名相關的統計,就抓瞎了。
java語言實現:
Java代碼
public class ShortUrlGenerator { /** * @param args */ public static void main(String[] args) { String sLongUrl = "http://474515923.qzone.qq.com" ; //長鏈接 String[] aResult = shortUrl (sLongUrl); // 打印出結果 for ( int i = 0; i < aResult. length ; i++) { System. out .println( "[" + i + "]:::" + aResult[i]); } } public static String[] shortUrl(String url) { // 可以自定義生成 MD5 加密字符傳前的混合 KEY String key = "mengdelong" ; // 要使用生成 URL 的字符 String[] chars = new String[] { "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" , "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" }; // 對傳入網址進行 MD5 加密 String sMD5EncryptResult = ( new CMyEncrypt()).md5(key + url); String hex = sMD5EncryptResult; String[] resUrl = new String[4]; for ( int i = 0; i < 4; i++) { // 把加密字符按照 8 位一組 16 進制與 0x3FFFFFFF 進行位與運算 String sTempSubString = hex.substring(i * 8, i * 8 + 8); // 這里需要使用 long 型來轉換,因為 Inteper .parseInt() 只能處理 31 位 , 首位為符號位 , 如果不用 long ,則會越界 long lHexLong = 0x3FFFFFFF & Long.parseLong (sTempSubString, 16); String outChars = "" ; for ( int j = 0; j < 6; j++) { // 把得到的值與 0x0000003D 進行位與運算,取得字符數組 chars 索引 long index = 0x0000003D & lHexLong; // 把取得的字符相加 outChars += chars[( int ) index]; // 每次循環按位右移 5 位 lHexLong = lHexLong >> 5; } // 把字符串存入對應索引的輸出數組 resUrl[i] = outChars; } return resUrl; } }
以下為php語言實現:
Php代碼
<?php function shorturl($input) { $base32 = array ( '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' ); $hex = md5($input); $hexLen = strlen($hex); $subHexLen = $hexLen / 8; $output = array(); for ($i = 0; $i < $subHexLen; $i++) { $subHex = substr ($hex, $i * 8, 8); $int = 0x3FFFFFFF & (1 * ('0x'.$subHex)); $out = ''; for ($j = 0; $j < 6; $j++) { $val = 0x0000001F & $int; $out .= $base32[$val]; $int = $int >> 5; } $output[] = $out; } return $output; } ?>
Php代碼
<?php function random($length, $pool = '') { $random = ''; if (emptyempty($pool)) { $pool = 'abcdefghkmnpqrstuvwxyz'; $pool .= '23456789'; } srand ((double)microtime()*1000000); for($i = 0; $i < $length; $i++) { $random .= substr($pool,(rand()%(strlen ($pool))), 1); } return $random; } ?>
跳轉原理
當我們生成短鏈接之后,只需要在表中(數據庫或者NoSql )存儲原始鏈接與短鏈接的映射關系即可。當我們訪問短鏈接時,只需要從映射關系中找到原始鏈接,即可跳轉到原始鏈接。
附:控制長度?
package testjava; import java.util.HashMap; import java.util.Random; /* * URL Shortener */ public class URLShortener { // storage for generated keys private HashMap<String, String> keyMap; // key-url map private HashMap<String, String> valueMap;// url-key map to quickly check // whether an url is // already entered in our system private String domain; // Use this attribute to generate urls for a custom // domain name defaults to http://fkt.in private char myChars[]; // This array is used for character to number // mapping private Random myRand; // Random object used to generate random integers private int keyLength; // the key length in URL defaults to 8 // Default Constructor URLShortener() { keyMap = new HashMap<String, String>(); valueMap = new HashMap<String, String>(); myRand = new Random(); keyLength = 8; myChars = new char[62]; for (int i = 0; i < 62; i++) { int j = 0; if (i < 10) { j = i + 48; } else if (i > 9 && i <= 35) { j = i + 55; } else { j = i + 61; } myChars[i] = (char) j; } domain = "http://fkt.in"; } // Constructor which enables you to define tiny URL key length and base URL // name URLShortener(int length, String newDomain) { this(); this.keyLength = length; if (!newDomain.isEmpty()) { newDomain = sanitizeURL(newDomain); domain = newDomain; } } // shortenURL // the public method which can be called to shorten a given URL public String shortenURL(String longURL) { String shortURL = ""; if (validateURL(longURL)) { longURL = sanitizeURL(longURL); if (valueMap.containsKey(longURL)) { shortURL = domain + "/" + valueMap.get(longURL); } else { shortURL = domain + "/" + getKey(longURL); } } // add http part return shortURL; } // expandURL // public method which returns back the original URL given the shortened url public String expandURL(String shortURL) { String longURL = ""; String key = shortURL.substring(domain.length() + 1); longURL = keyMap.get(key); return longURL; } // Validate URL // not implemented, but should be implemented to check whether the given URL // is valid or not boolean validateURL(String url) { return true; } // sanitizeURL // This method should take care various issues with a valid url // e.g. www.google.com,www.google.com/, http://www.google.com, // http://www.google.com/ // all the above URL should point to same shortened URL // There could be several other cases like these. String sanitizeURL(String url) { if (url.substring(0, 7).equals("http://")) url = url.substring(7); if (url.substring(0, 8).equals("https://")) url = url.substring(8); if (url.charAt(url.length() - 1) == '/') url = url.substring(0, url.length() - 1); return url; } /* * Get Key method */ private String getKey(String longURL) { String key; key = generateKey(); keyMap.put(key, longURL); valueMap.put(longURL, key); return key; } // generateKey private String generateKey() { String key = ""; boolean flag = true; while (flag) { key = ""; for (int i = 0; i <= keyLength; i++) { key += myChars[myRand.nextInt(62)]; } // System.out.println("Iteration: "+ counter + "Key: "+ key); if (!keyMap.containsKey(key)) { flag = false; } } return key; } // test the code public static void main(String args[]) { URLShortener u = new URLShortener(5, "www.tinyurl.com/"); String urls[] = { "www.google.com/", "www.google.com", "http://www.yahoo.com", "www.yahoo.com/", "www.amazon.com", "www.amazon.com/page1.php", "www.amazon.com/page2.php", "www.flipkart.in", "www.rediff.com", "www.techmeme.com", "www.techcrunch.com", "www.lifehacker.com", "www.icicibank.com" }; for (int i = 0; i < urls.length; i++) { System.out.println("URL:" + urls[i] + "\tTiny: " + u.shortenURL(urls[i]) + "\tExpanded: " + u.expandURL(u.shortenURL(urls[i]))); } } }