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