- Article -

记录一个短链算法

分类于 后端开发 标签 shorturl 短链 发表于2020-05-07 20:30

因为自己的这个博客没有依赖数据库,所有的文章都是实时读取的md文件,因此灵活的分类就会导致路径名称过长或者是一串中文,如: /article?path=%2f后端开发%2fGOLANG%2f前端自动部署工具MareWood开源.md 某些地方还会转码为 %2f%e5%89%8d%e7%ab%af%e5%bc%80%e5%8f%91%2fREACT%e5%92%8cVUE%e7%9a%84%e5%a4%9a%e5%a5%97%e6%89%93%e5%8c%85%e7%8e%af%e5%a2%83.md 看起来不仅不美观还长,因此需要做成短链的形式。

目前短链有两种算法,自增ID法和摘要算法

自增ID

设置 id 自增,一个 10进制 id 对应一个 62进制的数值,1对1,也就不会出现重复的情况。这个利用的就是低进制转化为高进制时,字符数会减少的特性,短址的长度一般设为 6 位,而每一位是由 [a - z, A - Z, 0 - 9] 总共 62 个字母组成的,所以 6 位的话,总共会有 62^6 ~= 568亿种组合,基本上够用了。

摘要算法

这种算法,虽然会生成4个,但是仍然存在重复几率

自增ID一般需要数据库的自增ID配合,因此我们选择摘要算法,当然,对于一个博客来说,根本不需要考虑重复的问题,这才几百个路径,互联网那么多url都不怕。

package short

import (
	"crypto/md5"
	"fmt"
	"strconv"
)

const charset = "A0a12B3b4CDc56Ede7FGf8Hg9IhJKiLjkMNlmOPnQRopqrSstTuvUVwxWXyYzZ"

func generateCharset(url, hexMd5 string, len, sectionNum int, cb func(url, keyword string) bool) string {
	for i := 0; i < sectionNum; i++ {
		sectionHex := hexMd5[i*8:8+i*8]
		bits, _ := strconv.ParseUint(sectionHex, 16, 32)
		bits = bits & 0x3FFFFFFF
		keyword := ""
		for j := 0; j < len; j++ {
			idx := bits & 0x3D
			keyword = keyword + string(charset[idx])
			bits = bits >> 5
		}
		if cb(url, keyword) {
			return keyword
		}
	}
	return ""
}
// 起初生成6位的短码,当四组6位短码都重复时,再生成8位的短码,因此总共会有8个短码供你选择。

func GenerateShortUrl(url string, cb func(url, keyword string) bool) string {
	if url == "" || cb == nil {
		return ""
	}
	hexMd5 := fmt.Sprintf("%x", md5.Sum([]byte(url)))
	sections := len(hexMd5)/8

	keyword := generateCharset(url, hexMd5, 6,sections, cb)
	if keyword == "" {
		return generateCharset(url, hexMd5, 8,sections, cb)
	}
	return keyword
}