Go語(yǔ)言實(shí)現(xiàn)有規(guī)律的數(shù)字版本號(hào)的排序工具
前言
在某些場(chǎng)景,我們可能需要對(duì)版本號(hào)進(jìn)行排序。版本號(hào)的形式有很多種,例如:
- 1.0.0, 1.0.1.1, 2.0.1.1
- v1.0.0, v1.10.1, v2.0
- ······
而本文所介紹的版本號(hào)排序工具,是針對(duì)有規(guī)律的數(shù)字版本號(hào)如 1.0.0, 1, 2.15.0 這種形式。
創(chuàng)作解讀
版本號(hào)的大小比較與排序
版本號(hào)排序的前提,首先得比較兩個(gè)版本號(hào)的大小。由于版本號(hào)長(zhǎng)度可能不一致,所以需要額外做一些處理。對(duì)于版本號(hào)的比較,我的算法思路是:
1、以 . 為分隔符,將版本號(hào)的每段數(shù)字存到切片里,方便后續(xù)比較大小。例如 "1.0" → ["1", "0"],"1.0.1" → ["1", "0", "1"]。
firstVersions := strings.Split(versions[i], ".") secondVersions := strings.Split(versions[j], ".")
2、由于兩個(gè)版本號(hào)的長(zhǎng)度可能不一致,因此需要做 填充0,統(tǒng)一長(zhǎng)度 的操作。所以第二步就是獲取兩個(gè)版本號(hào)中,最大長(zhǎng)度,然后對(duì)長(zhǎng)度最小的版本號(hào)切片,填充零,保持兩個(gè)版本號(hào)的長(zhǎng)度一致。例如第一步的兩個(gè)版本號(hào) ["1", "0"]、["1", "0", "1"],需要對(duì)第一個(gè)版本號(hào)填充一個(gè)零(填充之后的結(jié)果 → ["1", "0", "0"]),才能保持兩個(gè)版本號(hào)的長(zhǎng)度一致,方便后續(xù)比較。
// 0 填充
// 獲取最大長(zhǎng)度并向最小長(zhǎng)度的切片填充 "0",統(tǒng)一長(zhǎng)度
func getMaxAndFillZero(s1 *[]string, s2 *[]string) int {
len1, len2 := len(*s1), len(*s2)
if len1 > len2 {
fillZero(s2, len1-len2)
return len1
}
fillZero(s1, len2-len1)
return len2
}
// 0 填充
func fillZero(s *[]string, size int) {
for i := 0; i < size; i++ {
*s = append(*s, "0")
}
}size 為最大長(zhǎng)度 - 最小長(zhǎng)度的值,也就是要填充 0 的個(gè)數(shù)。
3、遍歷切片,從前依次比較兩個(gè)版本號(hào)每段數(shù)字的大小。
如果第一個(gè)版本號(hào)的第一段數(shù)字大于或小于第二個(gè)版本號(hào)的第二段數(shù)字,則可以根據(jù)排序規(guī)則決定兩個(gè)版本號(hào)的先后位置。
如果相等,則比較下一段數(shù)字的大小,以此類(lèi)推。
for k := 0; k < maxLen; k++ {
// 由于上面判斷了版本號(hào)的合法性,因此 error 可以忽略
vi, _ := strconv.Atoi(firstVersions[k])
vj, _ := strconv.Atoi(secondVersions[k])
if vi < vj {
if sortRule == DESC {
// 降序排序
// todo 交換操作
}
// 默認(rèn)升序排序,即使 sortRule 不是 ASC
// todo 交換操作
} else if vi > vj {
// 降序排序
if sortRule == DESC {
// todo 交換操作
}
// 默認(rèn)升序排序,即使 sortRule 不是 ASC
// todo 交換操作
}
}對(duì)字符串切片的排序,本工具使用的函數(shù)是 SliceStable(x any, less func(i, j int) bool),通過(guò)此函數(shù),可以自定義比較大小的規(guī)則。
sort.SliceStable(versions, func(i, j int) bool {
firstVersions := strings.Split(versions[i], ".")
secondVersions := strings.Split(versions[j], ".")
// 判斷版本號(hào)格式的合法性
isNormal(firstVersions)
isNormal(secondVersions)
// 獲取最大值并填充 "0", 統(tǒng)一長(zhǎng)度
maxLen := getMaxAndFillZero(&firstVersions, &secondVersions)
for k := 0; k < maxLen; k++ {
// 由于上面判斷了版本號(hào)的合法性,因此 error 可以忽略
vi, _ := strconv.Atoi(firstVersions[k])
vj, _ := strconv.Atoi(secondVersions[k])
if vi < vj {
if sortRule == DESC {
// 降序排序
return false
}
// 默認(rèn)升序排序,即使 sortRule 不是 ASC
return true
} else if vi > vj {
// 降序排序
if sortRule == DESC {
return true
}
// 默認(rèn)升序排序,即使 sortRule 不是 ASC
return false
}
}
return false
})版本號(hào)的合法性校驗(yàn)
由于本工具處理的版本號(hào)是有規(guī)律的數(shù)字版本號(hào),如果版本號(hào)包含字母或其他特殊字符,會(huì)影響到排序的進(jìn)行,因此需要提前對(duì)版本號(hào)進(jìn)行合法性的校驗(yàn)。
// 判斷版本號(hào)的格式是否合法
func isNormal(versions []string) {
for _, v := range versions {
for _, r := range []rune(v) {
if !unicode.IsNumber(r) {
panic(errors.New("版本號(hào)格式錯(cuò)誤:" + string(r)))
}
}
}
}遍歷每段版本號(hào),然后對(duì)每段版本號(hào)的字符進(jìn)行遍歷,判斷是否是數(shù)字,如果不是,則 panic 掉,結(jié)束排序。
錯(cuò)誤處理
由于版本號(hào)的不合法性,可能會(huì)程序運(yùn)行的過(guò)程中產(chǎn)生錯(cuò)誤。因此,有必要人工捕獲錯(cuò)誤,提高工具的健壯性。
版本號(hào)排序函數(shù)提供一個(gè) error 的返回值,用于判斷是否產(chǎn)生錯(cuò)誤。錯(cuò)誤的捕獲邏輯如下:
defer func() {
if r := recover(); r != nil {
if er, ok := r.(error); ok {
err = er
} else {
err = errors.New("")
fmt.Println("未知錯(cuò)誤: ")
fmt.Println(r)
}
}
}()捕獲版本號(hào)的合法性校驗(yàn)時(shí)主動(dòng) panic 的錯(cuò)誤,并結(jié)束排序。
總結(jié)
- 本工具實(shí)現(xiàn)了對(duì)有規(guī)律的數(shù)字版本號(hào)集合進(jìn)行排序。
- 在排序的過(guò)程中,由于版本號(hào)的長(zhǎng)度可能不一致,因此執(zhí)行填充
0操作,統(tǒng)一長(zhǎng)度,再進(jìn)行版本號(hào)的大小比較; - 除此之外,還對(duì)版本號(hào)的合法性做了校驗(yàn),捕獲可預(yù)知和不可預(yù)知的
panic錯(cuò)誤,提高了工具的健壯性。 - 經(jīng)測(cè)試,核心功能已實(shí)現(xiàn),但有些地方還能改進(jìn),后續(xù)會(huì)對(duì)代碼進(jìn)行優(yōu)化。
到此這篇關(guān)于Go語(yǔ)言實(shí)現(xiàn)有規(guī)律的數(shù)字版本號(hào)的排序工具的文章就介紹到這了,更多相關(guān)Go語(yǔ)言排序工具內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Gin+Gorm實(shí)現(xiàn)CRUD的實(shí)戰(zhàn)
本文主要介紹了Gin+Gorm實(shí)現(xiàn)CRUD的實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
go 對(duì)象池化組件 bytebufferpool使用詳解
這篇文章主要為大家介紹了go 對(duì)象池化組件 bytebufferpool使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Go語(yǔ)言學(xué)習(xí)之context包的用法詳解
日常Go開(kāi)發(fā)中,Context包是用的最多的一個(gè)了,幾乎所有函數(shù)的第一個(gè)參數(shù)都是ctx,那么我們?yōu)槭裁匆獋鬟fContext呢,Context又有哪些用法,底層實(shí)現(xiàn)是如何呢?相信你也一定會(huì)有探索的欲望,那么就跟著本篇文章,一起來(lái)學(xué)習(xí)吧2022-10-10
golang解析json數(shù)據(jù)的4種方法總結(jié)
在日常工作中每一名開(kāi)發(fā)者,不管是前端還是后端,都經(jīng)常使用 JSON,下面這篇文章主要給大家介紹了關(guān)于golang解析json數(shù)據(jù)的4種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
300行代碼實(shí)現(xiàn)go語(yǔ)言即時(shí)通訊聊天室
本文主要介紹了300行代碼實(shí)現(xiàn)go語(yǔ)言即時(shí)通訊聊天室,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
golang elasticsearch Client的使用詳解
這篇文章主要介紹了golang elasticsearch Client的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05

