最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析Go語(yǔ)言如何在終端里實(shí)現(xiàn)倒計(jì)時(shí)

 更新時(shí)間:2025年03月06日 08:34:27   作者:apocelipes  
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中是如何在終端里實(shí)現(xiàn)倒計(jì)時(shí)的,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

最近在更新系統(tǒng)的時(shí)候發(fā)現(xiàn)pacman的命令行界面變了,我有很久沒(méi)更新過(guò)設(shè)備上的Linux系統(tǒng)了,所以啥時(shí)候變的不好說(shuō)。但這一變化成功勾起了我的好奇心。新版的更新進(jìn)度界面如下:

新的更新進(jìn)度界面能同時(shí)顯示多個(gè)進(jìn)度條,而且并沒(méi)有依靠ncurses這個(gè)傳統(tǒng)的TUI庫(kù)。為啥我能斷定沒(méi)有用ncurses呢,因?yàn)橛眠^(guò)這個(gè)庫(kù)的人都會(huì)發(fā)現(xiàn)程序在繪制界面的時(shí)候會(huì)用背景色清屏,且退出后終端的內(nèi)容會(huì)恢復(fù)成運(yùn)行程序前的樣子,而上述表現(xiàn)都不存在。

不借助專(zhuān)用的庫(kù)卻又能繪制出比較生動(dòng)的效果,這難道不吸引人嗎?

所以帶著好奇心,我簡(jiǎn)單探索了實(shí)現(xiàn)的原理,并且用相同的原理做了個(gè)新東西:

這是一個(gè)在終端中顯示倒計(jì)時(shí)的小玩具,原理和pacman的進(jìn)度條是一樣的,我并沒(méi)有一比一去復(fù)現(xiàn)pacman的效果,那樣其實(shí)和對(duì)著范本寫(xiě)作文一樣略顯無(wú)聊,所以我選擇活用知識(shí)做個(gè)新玩具。

好了,我們先來(lái)復(fù)習(xí)下單個(gè)終端命令行的進(jìn)度條是怎么實(shí)現(xiàn)的。

單個(gè)進(jìn)度條的原理其實(shí)很簡(jiǎn)單,幾乎所有的終端和終端模擬器都支持一些特殊的控制字符,比如\n表示新加一個(gè)空白行并把光標(biāo)移動(dòng)到這個(gè)新行的最左側(cè)也就是開(kāi)頭處;\r則是將光標(biāo)移動(dòng)到當(dāng)前行的開(kāi)頭處。

所以單個(gè)進(jìn)度條的繪制過(guò)程一共只要兩步:

  • 根據(jù)進(jìn)度計(jì)算出當(dāng)前進(jìn)度條的樣子,然后用打印函數(shù)輸出,注意不能輸出換行符\n
  • 輸出\r讓光標(biāo)回到行首,等待一段時(shí)間,重復(fù)步驟1,新的輸出內(nèi)容會(huì)覆蓋掉老的。
  • 進(jìn)度到了100%之后就可以輸出一個(gè)換行符\n結(jié)束進(jìn)度條的打印了。

最關(guān)鍵的地方也只有一處,新的輸出內(nèi)容的長(zhǎng)度要大于或者等于老內(nèi)容,否則老內(nèi)容會(huì)殘留在終端里。

人眼的要求很低,所以你甚至可以不必做到每秒xx次刷新,只要在一秒或幾秒里更新幾次就能讓人覺(jué)得你的進(jìn)度條動(dòng)起來(lái)了。

所以一個(gè)最簡(jiǎn)單的例子可以是這樣的:

package main
 
import (
	"bytes"
	"fmt"
	"time"
)
 
const width = 50
 
func main() {
	bar := bytes.Repeat([]byte{' '}, width)
	fmt.Println()
	for i := range 50 {
		bar[i] = '='
		fmt.Printf("[%s] % 3d%%\r", bar, (i+1)*2)
		time.Sleep(100 * time.Millisecond)
	}
    fmt.Println()
    fmt.Println("end")
}

這是效果:

\r有個(gè)缺點(diǎn),它只能回溯當(dāng)前行,而且這個(gè)“行”是以終端顯示為準(zhǔn)的——即使你的輸出并沒(méi)有包含換行符但它的長(zhǎng)度超過(guò)了終端顯示的寬度導(dǎo)致需要“折行”,那么新折行出來(lái)的那行在終端顯示中會(huì)被認(rèn)為是一個(gè)新行,\r只會(huì)將光標(biāo)放到這個(gè)新行的開(kāi)頭。

其實(shí)我最開(kāi)始想利用折行加\r字符實(shí)現(xiàn)多行進(jìn)度條,但很快就發(fā)現(xiàn)這條路是走不通的。顯然pacman并沒(méi)有使用\r或者說(shuō)它還利用了一些其他的東西。

看源代碼是最快的,而且簡(jiǎn)單搜索一下“progressbar”很快就能找到答案。我就不賣(mài)關(guān)子了,pacman實(shí)現(xiàn)多行進(jìn)度條效果是利用了ASNI轉(zhuǎn)義序列。

ANSI轉(zhuǎn)義序列(ANSI escape sequences)是一種帶內(nèi)信號(hào)的轉(zhuǎn)義序列標(biāo)準(zhǔn),用于控制視頻文本終端上的光標(biāo)位置、顏色和其他選項(xiàng)。在文本中嵌入確定的字節(jié)序列,大部分以ESC轉(zhuǎn)義字符和"["字符開(kāi)始,終端會(huì)把這些字節(jié)序列解釋為相應(yīng)的指令,而不是普通的字符編碼。

簡(jiǎn)單的說(shuō),轉(zhuǎn)義序列就像一些命令,可以控制光標(biāo)和終端的各種行為。

具體格式是:轉(zhuǎn)義序列開(kāi)始字符參數(shù)1;參數(shù)2;...;參數(shù)N命令。我們最常見(jiàn)的轉(zhuǎn)義序列是顏色控制,讓終端里的文字變成紅色:\033[0;31m。其中\033[是轉(zhuǎn)義序列的開(kāi)始標(biāo)志,0;31是命令m的兩個(gè)參數(shù),參數(shù)之間用空格分隔,最后一個(gè)參數(shù)緊貼著命令。

轉(zhuǎn)義序列的支持程度要看終端和終端模擬器,好消息是我們需要用到的轉(zhuǎn)義序列的被廣泛支持的,我們要用它們來(lái)在行與行之間移動(dòng)光標(biāo)并繪制內(nèi)容。

轉(zhuǎn)義序列支持光標(biāo)上下左右移動(dòng)還支持直接清除整行的內(nèi)容,這使得我們可以將終端當(dāng)成一個(gè)畫(huà)布:每個(gè)字符的位置相當(dāng)于畫(huà)布上的一個(gè)像素點(diǎn)(因此使用等寬字體效果顯示會(huì)更好),坐標(biāo)原點(diǎn)是程序運(yùn)行開(kāi)始后光標(biāo)所在的位置,根據(jù)這個(gè)原點(diǎn)可以簡(jiǎn)單構(gòu)建出一個(gè)平面坐標(biāo)系,我們可以用一些特殊字符模擬點(diǎn)和線來(lái)繪制簡(jiǎn)單的圖形。

我們要用的轉(zhuǎn)義序列是這些:

  • \033[nF,將光標(biāo)向上移動(dòng)n行
  • \033[nE,將光標(biāo)向下移動(dòng)n行
  • \033[nC,將光標(biāo)向后(右)移動(dòng)n個(gè)字符
  • \033[2K,清除光標(biāo)所在行的整個(gè)內(nèi)容(2以外的參數(shù)可以選擇只清除光標(biāo)前/后的內(nèi)容)
  • 轉(zhuǎn)義字符之間可以組合使用,比如\033[nE\033[mC表示光標(biāo)先向下移動(dòng)n行然后再向右移動(dòng)m個(gè)字符。

現(xiàn)在你應(yīng)該明白那個(gè)倒計(jì)時(shí)是怎么畫(huà)出來(lái)的了,核心技術(shù)點(diǎn)就是找到個(gè)合適的數(shù)字asciiart,然后根據(jù)每秒更新的內(nèi)容在正確的位置上用上面的轉(zhuǎn)義序列像畫(huà)像素點(diǎn)一樣把數(shù)字和分隔符畫(huà)出來(lái)就行了。

說(shuō)說(shuō)其實(shí)一句話(huà)的事情,但做起來(lái)還是比較麻煩的,因?yàn)檗D(zhuǎn)義序列用的都是相對(duì)坐標(biāo),稍微算錯(cuò)一點(diǎn)相對(duì)位置顯示效果就整個(gè)完蛋了,我也是調(diào)試了三四回才做到正確繪制的:

func (ar *ASCIIArtCharRender) RenderContent(duration time.Duration) {
	if len(ar.chars) > 0 {
		ar.chars = ar.chars[:0]
	}
	ar.chars = char.ConvertToChars(duration, char.ASCIIArtChars, ar.chars)
	for i := 0; i < char.MaxASCIIArtCharHeight(); i++ {
		util.CursorEraseEntireLine()
		fmt.Print(ar.chars[0][i])
		fmt.Print(" ")
		fmt.Print(ar.chars[1][i])
		fmt.Print("  ")
		fmt.Print(char.ASCIIArtChars[char.ASCIIArtColonIdx][i])
		fmt.Print("  ")
		fmt.Print(ar.chars[2][i])
		fmt.Print(" ")
		fmt.Print(ar.chars[3][i])
		fmt.Print("  ")
		fmt.Print(char.ASCIIArtChars[char.ASCIIArtColonIdx][i])
		fmt.Print("  ")
		fmt.Print(ar.chars[4][i])
		fmt.Print(" ")
		fmt.Print(ar.chars[5][i])
		fmt.Print("\n")
	}
}
 
func (ar *ASCIIArtCharRender) RenderFlashing() {
	util.CursorDownForward(1, 3+len(ar.chars[0][0])+1+len(ar.chars[1][0]))
	fmt.Print(" ")
	util.CursorForward(3 + len(ar.chars[2][0]) + 1 + len(ar.chars[3][0]) + 3)
	fmt.Print(" ")
	util.CursorDownForward(1, 2+len(ar.chars[0][0])+1+len(ar.chars[1][0]))
	fmt.Print("   ")
	util.CursorForward(2 + len(ar.chars[2][0]) + 1 + len(ar.chars[3][0]) + 2)
	fmt.Print("   ")
 
	util.CursorDownForward(2, 3+len(ar.chars[0][0])+1+len(ar.chars[1][0]))
	fmt.Print(" ")
	util.CursorForward(3 + len(ar.chars[2][0]) + 1 + len(ar.chars[3][0]) + 3)
	fmt.Print(" ")
	util.CursorDownForward(1, 2+len(ar.chars[0][0])+1+len(ar.chars[1][0]))
	fmt.Print("   ")
	util.CursorForward(2 + len(ar.chars[2][0]) + 1 + len(ar.chars[3][0]) + 2)
	fmt.Print("   ")
	// move to bottom
	util.CursorDown(1)
}

第一個(gè)函數(shù)是繪制時(shí)間用的數(shù)字的,為了簡(jiǎn)單我已經(jīng)提前把數(shù)字的asciiart保存進(jìn)了二維數(shù)組并且做到了等高,這樣畫(huà)的時(shí)候只要知道需要什么數(shù)字就行,剩下的就是逐行輸出“像素點(diǎn)”。

第二個(gè)函數(shù)是用來(lái)繪制電子時(shí)鐘數(shù)字分隔符的閃爍效果的,這個(gè)看上去就更亂了,因?yàn)樾枰诮K端畫(huà)布上大范圍移動(dòng)。

所以會(huì)者不難,純體力活。

完整的代碼可以在這找到:https://github.com/apocelipes/ascii-count-down,歡迎各位大佬的改進(jìn)或者功能增強(qiáng)。

總結(jié)

TUI還是挺有意思的,好玩能學(xué)到東西而且很能消磨無(wú)聊的時(shí)間。

另外我覺(jué)得在之間看源碼對(duì)答案之前,可以先自己思考一下并動(dòng)手做做試驗(yàn)比如像我那樣最先異想天開(kāi)用折行去實(shí)現(xiàn)多行進(jìn)度條。這樣雖然浪費(fèi)了點(diǎn)時(shí)間,但可以加深自己對(duì)新知識(shí)的理解和記憶。

到此這篇關(guān)于淺析Go語(yǔ)言如何在終端里實(shí)現(xiàn)倒計(jì)時(shí)的文章就介紹到這了,更多相關(guān)Go終端實(shí)現(xiàn)倒計(jì)時(shí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 5個(gè)可以在Golang中優(yōu)化代碼以提高性能的技巧分享

    5個(gè)可以在Golang中優(yōu)化代碼以提高性能的技巧分享

    作為一名軟件工程師,確保你的代碼高效且性能良好是非常重要的。本文主要和大家分享5個(gè)可以在Golang中優(yōu)化代碼以提高性能的技巧,希望對(duì)大家有所幫助
    2023-03-03
  • 深入解析Go template模板使用詳解

    深入解析Go template模板使用詳解

    這篇文章主要介紹了深入解析Go template模板使用詳解,需要的朋友可以參考下
    2022-04-04
  • 詳解Golang 中的并發(fā)限制與超時(shí)控制

    詳解Golang 中的并發(fā)限制與超時(shí)控制

    這篇文章主要介紹了詳解Golang 中的并發(fā)限制與超時(shí)控制,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • 分析Go錯(cuò)誤處理優(yōu)化go?recover機(jī)制缺陷

    分析Go錯(cuò)誤處理優(yōu)化go?recover機(jī)制缺陷

    這篇文章主要為大家介紹了分析Go錯(cuò)誤處理優(yōu)化go?recover機(jī)制缺陷示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Golang HTTP 服務(wù)平滑重啟及升級(jí)的思路

    Golang HTTP 服務(wù)平滑重啟及升級(jí)的思路

    Golang HTTP服務(wù)在上線時(shí),需要重新編譯可執(zhí)行文件,關(guān)閉正在運(yùn)行的進(jìn)程,然后再啟動(dòng)新的運(yùn)行進(jìn)程。這篇文章主要介紹了Golang HTTP 服務(wù)平滑重啟及升級(jí),需要的朋友可以參考下
    2020-04-04
  • golang中日期操作之日期格式化及日期轉(zhuǎn)換

    golang中日期操作之日期格式化及日期轉(zhuǎn)換

    在編程中,程序員會(huì)經(jīng)常使用到日期相關(guān)操作,下面這篇文章主要給大家介紹了關(guān)于golang中日期操作之日期格式化及日期轉(zhuǎn)換的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • 在Golang中正確的修改HTTPRequest的Host的操作方法

    在Golang中正確的修改HTTPRequest的Host的操作方法

    我們工作中經(jīng)常需要通過(guò)HTTP請(qǐng)求Server的服務(wù),比如腳本批量請(qǐng)求接口跑數(shù)據(jù),由于一些網(wǎng)關(guān)策略,部分Server會(huì)要求請(qǐng)求中Header里面附帶Host參數(shù),所以本文給大家介紹了如何在Golang中正確的修改HTTPRequest的Host,需要的朋友可以參考下
    2023-12-12
  • Go語(yǔ)言中次方表示的實(shí)現(xiàn)

    Go語(yǔ)言中次方表示的實(shí)現(xiàn)

    本文主要介紹了Go語(yǔ)言中次方表示的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • 為什么Go語(yǔ)言把類(lèi)型聲明放在后面?

    為什么Go語(yǔ)言把類(lèi)型聲明放在后面?

    今天小編就為大家分享一篇關(guān)于為什么Go語(yǔ)言把類(lèi)型聲明放在后面?,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • 掌握GoLang Fiber路由和中間件技術(shù)進(jìn)行高效Web開(kāi)發(fā)

    掌握GoLang Fiber路由和中間件技術(shù)進(jìn)行高效Web開(kāi)發(fā)

    這篇文章主要為大家介紹了GoLang Fiber路由和中間件進(jìn)行高效Web開(kāi)發(fā),本文將深入探討 Fiber 中的路由細(xì)節(jié),學(xué)習(xí)如何創(chuàng)建和處理路由,深入了解使用路由參數(shù)的動(dòng)態(tài)路由,并掌握在 Fiber 應(yīng)用程序中實(shí)現(xiàn)中間件的藝術(shù)
    2024-01-01

最新評(píng)論

穆棱市| 西宁市| 荣成市| 满城县| 曲阜市| 曲阳县| 祁阳县| 潮州市| 海丰县| 扎赉特旗| 霞浦县| 健康| 双柏县| 崇州市| 南投县| 滦平县| 于田县| 西和县| 虞城县| 崇义县| 亚东县| 炎陵县| 丹棱县| 香港 | 会东县| 澎湖县| 台中县| 房产| 三河市| 肃北| 融水| 南投县| 卓尼县| 北流市| 晋江市| 乌拉特中旗| 宁国市| 正蓝旗| 石柱| 池州市| 瑞金市|