一、前言
在 Linux 上寫下一個(gè)簡(jiǎn)易的進(jìn)度條小程序。
成品展示:
今天的內(nèi)容比較輕松,只需要了解兩個(gè)知識(shí)點(diǎn),這個(gè)小程序就很容易寫出來了,讓我們開始今天的學(xué)習(xí)。
二、理解 ‘ ’ 與 ‘ ’
C 語言中有很多字符,而字符大體分為兩類:可顯字符、控制字符。
控制字符不可顯示,例如 和 就是控制字符。
而在我們平時(shí)打字時(shí),一行寫滿了需要換行,但是新起一行有很多種,例如:
這樣雖然新起一行了,但是不是我們想要的結(jié)果。
我們通常新起一行是在第二行的最左端,但是對(duì)于這個(gè)結(jié)果其實(shí)有兩個(gè)操作:
1.跳轉(zhuǎn)到第二行
2.回到第二行的最左端
有了這個(gè)基本概念,再來談 和 的作用:
:回車 - 回到文本行的開頭
:換行 - 新起一行
所以,其實(shí)我們平時(shí)泛指的換行實(shí)際上是 回車 + 換行。
且在語言范疇下,例如 C 語言,換行就可以達(dá)到回車 + 換行的效果。在平常,這一操作還是兩個(gè)步驟。
三、行緩沖
行緩沖這個(gè)概念認(rèn)識(shí)。
1、提出問題
首先先了解一下兩個(gè)庫函數(shù):
sleep:Linux 下的休眠函數(shù),單位是秒。頭文件為#include
fflush:刷新緩沖區(qū)
代碼 1:
#includeintmain() { printf("helloxxx"); sleep(3); return0; }
現(xiàn)象:
分析:
光標(biāo)停留在文本行的開頭,但是字符串沒有被打印。
反而像是 sleep 函數(shù)先起作用,然后 printf 函數(shù)再從光標(biāo)處開始打印。
打印完之后,shell 提示符緊跟著字符串后顯示。
代碼 2:
#includeintmain() { printf("helloxxx "); sleep(3); return0; }
現(xiàn)象:
分析:
printf 打印的字符串先顯示在終端上,光標(biāo)位于字符串的下一行。
sleep 函數(shù)使程序休眠 3 秒后,shell 提示符從光標(biāo)位置開始顯示。
代碼 3:
#includeintmain() { printf("helloxxx "); sleep(3); return0; }
現(xiàn)象:
分析:
printf 打印的字符串沒有顯示到終端,光標(biāo)一直停留在該打印字符串的一行
sleep 函數(shù)休眠三秒后,shell 提示符直接打印在了屏幕上。并沒有看到字符串。
觀察上面的現(xiàn)象,我們提出幾個(gè)問題:
代碼 1 好像是先執(zhí)行了 sleep ,在執(zhí)行 printf ,是這樣嗎?
代碼 2 加上了 ‘ ’ ,字符串一開始就顯示了,為什么?
代碼 3 好像什么都沒打印,這是為什么?
在解答這些問題之后,我們先了解一下行緩沖。
2、認(rèn)識(shí)行緩沖
在內(nèi)存中預(yù)留了一塊空間,用來緩沖輸入或輸出的數(shù)據(jù),這個(gè)保留的空間被稱為緩沖區(qū)。
我們之前或多或少都聽說過緩沖區(qū)。
在代碼 1 中,由于程序是按照數(shù)據(jù)執(zhí)行的,所以必定是先執(zhí)行 printf 。
但是數(shù)據(jù)沒有顯示,所以這時(shí)候,數(shù)據(jù)就一定被保存在某個(gè)位置,保存的位置就是緩沖區(qū)。
而要讓數(shù)據(jù)顯示,是需要刷新緩沖區(qū)的。
行緩沖是緩沖區(qū)刷新策略的一種,在行緩沖模式下,當(dāng)輸入和輸出中遇到 ‘ ’ 換行時(shí),就會(huì)刷新緩沖區(qū)。
有了這個(gè)概念,我們繼續(xù)分析問題。
3、解答與拓展
解答:
問題 1:代碼 1 好像是先執(zhí)行了 sleep ,在執(zhí)行 printf ,是這樣嗎?
當(dāng)然不是。由于程序是按照順序執(zhí)行的,所以必定是先執(zhí)行完 printf 在執(zhí)行 sleep 。
而數(shù)據(jù)沒有被顯示出來的原因是:數(shù)據(jù)保存在緩沖區(qū)中,但是沒有主動(dòng)刷新,當(dāng)程序退出后,保存在緩沖區(qū)中的數(shù)據(jù)被自動(dòng)刷新出來了。
所以才會(huì)造成這種現(xiàn)象。
問題 2:代碼 2 加上了 ,字符串一開始就顯示了,為什么?
這里由于是直接往顯示器上打印,所以采用的刷新方式為行緩沖。
所以執(zhí)行碰到 ‘ ’ 時(shí),就會(huì)把在緩沖區(qū)中的 (換行符之前) 的內(nèi)容全部刷新出來。
所以這段代碼一開始就會(huì)有數(shù)據(jù)顯示,然后再 sleep 休眠。
問題 3:代碼 3 好像什么都沒打印,這是為什么?
之前說過 是換行,所以當(dāng) printf 遇到 時(shí),就把光標(biāo)移到開頭。sleep 睡眠后,當(dāng)程序退出,shell 打印提示符時(shí),就覆蓋了字符串。
拓展:
數(shù)據(jù)真的是臨時(shí)保留在緩沖區(qū)里的嗎?光標(biāo)如何理解?
我們用一段代碼來理解這兩個(gè)問題:
#include#include intmain() { printf("helloxxx "); fflush(stdout); sleep(3); return0; }
現(xiàn)象:
觀察現(xiàn)象,我們發(fā)現(xiàn)當(dāng)我們使用fflush主動(dòng)刷新緩沖區(qū)后,數(shù)據(jù)就顯示在了屏幕上;且因?yàn)?‘ ’ 的原因,光標(biāo)指向字符串開頭;當(dāng)打印 shell 提示符時(shí),就直接從光標(biāo)位置開始覆蓋。
所以對(duì)于這兩個(gè)問題,我們已經(jīng)得到了答案:
1.數(shù)據(jù)被臨時(shí)保存在于緩沖區(qū)中,通過刷新就可以顯示
2.數(shù)據(jù)是從光標(biāo)位置開始打印的。
一句話理解光標(biāo):光標(biāo)和顯示器匹配,光標(biāo)在哪里,顯示器打印的時(shí)候就從哪里開始打印。
4、倒計(jì)時(shí)
基于對(duì)上面的理解,我們先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的倒計(jì)時(shí)。
倒計(jì)時(shí)就是在屏幕上不斷顯示數(shù)字,每次在同一位置顯示,并將之前的數(shù)據(jù)覆蓋。
既然是每次要從頭開始覆蓋,那么就可以用 ‘ ’ 來實(shí)現(xiàn)每次回到行首,并且可以通過相應(yīng)的格式化控制顯示多位打印。
但是 ‘ ’ 不會(huì)主動(dòng)刷新,所以要用fflush函數(shù)主動(dòng)刷新緩沖區(qū)。
在每次刷新之后,使用 sleep 函數(shù),間隔一定的時(shí)間。
由此,我們可以很輕松寫出代碼,例如寫一個(gè)從 10 開始的倒計(jì)時(shí):
#include#include intmain() { inti=10; for(;i>=0;i--){ //位寬控制, 回到開頭 printf("%2d ",i); fflush(stdout);//主動(dòng)刷新 sleep(1);//休眠 } printf(" ");//換行,打印提示符 return0; }
四、進(jìn)度條
好了,接下來進(jìn)入正題,我們開始寫進(jìn)度條。
進(jìn)度條樣式:
主體樣式為兩個(gè)中括號(hào)包裹,中間=>推進(jìn)的方式呈現(xiàn),比如:[======>]
主體右側(cè)中括號(hào)位置保持不變,中間元素不斷推進(jìn),比如:[=> ]
顯示當(dāng)前加載進(jìn)度,用[num%]顯示,num 隨著進(jìn)度條的不斷推進(jìn)而變化
顯示加載樣式,可以利用一個(gè)旋轉(zhuǎn)的字符,例如[]的樣式,順時(shí)針不斷旋轉(zhuǎn)
大約呈現(xiàn)狀態(tài)為:[========>] [15%] []
采用多文件:
文件存放在proc目錄中
proc.h :函數(shù)聲明
proc.c :進(jìn)度條邏輯
main.c :函數(shù)調(diào)用
makefile準(zhǔn)備:
由于采用多文件,所以依賴關(guān)系可以寫成依賴文件列表的樣式:
分塊邏輯:
1.進(jìn)度條主體
預(yù)留進(jìn)度條大小為 100 個(gè)=,外加 1 個(gè)>,加上保存'?'的位置,用數(shù)組存儲(chǔ)為 102 個(gè)單位。
進(jìn)度條是一行中的,所以需要用到' ',每次都需要使用fllush主動(dòng)刷新緩沖區(qū)。
每次刷新出數(shù)據(jù)之后,將 = 填充到數(shù)組中,并且顯示 > 。在最后一次顯示時(shí),控制 > 不要顯示。
然后休眠一小會(huì)。由于休眠用 sleep 函數(shù)太慢。所以可以用 usleep 函數(shù)休眠,usleep 函數(shù)的參數(shù)單位是微秒。
根據(jù)這個(gè)寫出代碼:
2.百分比顯示
%% 顯示為一個(gè) % ,而 %d 為數(shù)字,這步很簡(jiǎn)單,只要在 printf 語句中加上內(nèi)容:
printf("[%-100s][%d%%] ",bar,i);
3.旋轉(zhuǎn)光標(biāo):
光標(biāo)旋轉(zhuǎn)方向?yàn)轫槙r(shí)針旋轉(zhuǎn),那么旋轉(zhuǎn)時(shí)就可以用數(shù)組保存。
旋轉(zhuǎn)每次顯示內(nèi)容分別為| / - ,\代表一個(gè) ,因?yàn)楹?結(jié)合的會(huì)被解析為轉(zhuǎn)義字符,將其保存到字符串中。
而由于字符串一共就四個(gè)字符,所以輸出的時(shí)候需要控制輸出位置。
代碼:
constchar*str="|/-";//字符串 printf("[%-100s][%d%%][%c] ",bar,i,str[i%4]);//輸出語句
完整代碼:
proc.h:
#pragmaonce #includeexternvoidprocess();
proc.c:
#include"proc.h" #include#include #defineSIZE102//數(shù)組大小 #defineSTYLE'=' #defineFLAG'>' voidprocess() { constchar*str="|/-"; charbar[SIZE]; memset(bar,'?',sizeof(bar)); inti=0; while(i<=?100)?{ ??????printf("[%-100s][%d%%][%c] ",?bar,?i,?str[i?%?4]);?//?格式控制 ??????fflush(stdout);?//?刷新 ??????bar[i++]?=?STYLE;?//?填充數(shù)據(jù) ??????if?(i?!=?100)?{ ??????????bar[i]?=?FLAG;?//?如果不是最后一次則顯示?> } usleep(100000);//休眠 } printf(" "); }
test.c
#include"proc.h" intmain() { process(); return0; }
進(jìn)度條展示:
-
Linux
+關(guān)注
關(guān)注
87文章
11329瀏覽量
209975 -
C語言
+關(guān)注
關(guān)注
180文章
7614瀏覽量
137266 -
字符
+關(guān)注
關(guān)注
0文章
233瀏覽量
25233 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4344瀏覽量
62813 -
小程序
+關(guān)注
關(guān)注
1文章
239瀏覽量
12192
原文標(biāo)題:教你在 Linux 上寫一個(gè)進(jìn)度條小程序
文章出處:【微信號(hào):良許Linux,微信公眾號(hào):良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論