我們在使用計算機不免有拷貝文件或者下載文件的時候,總會看到行行色色的進度條,如下圖的兩種:
下載文件:
拷貝文件:
在實現進度條之前首先要明白兩個問題:
- 回車與換行的區別
- printf的緩衝區問題
可能你不明白爲什麼一個小小的進度條關這倆兄弟什麼事,但請耐心往下看!
回車與換行
在計算機還沒有出現之前,有一種叫做電傳打字機(Teletype Model 33)的東西,每秒鐘可以打10個字符。但是它有一個問題,就是打完一行換行的時候要用去0.2秒,正好可以打兩個字符。要是在這0.2秒裏面,又有新的字符傳過來,那麼這個字符將會丟失。
爲了解決這個問題,研製人員想了個辦法,在每行的後面加兩個表示結束的字符。
- 換行—-告訴打字機把打印頭定位在左邊界;
- 回車—-告訴打字機把紙向下移動一行;
後來這個發明就被延續到了計算機上,但是因爲系統的不同,設計也有所不同。
- Unix系統—-結尾只有“換行”—-‘\n’;
- Mac系統—-結尾只有“回車”—-‘\r’;
- Windows系統—-結尾有是“回車”“換行”—-‘\r\n’;
所以,回車是‘\r’,而換行是‘\n’。我們平時在Windows下敲一下Enter鍵實際上是輸入了兩個字符‘\r\n’。
這對我們實現進度條有什麼用?
我們實現的這個簡答進度條的原理是將不同的字符數組以較短的時間t爲間隔,依次打印輸出至屏幕。
如我定義一個字符數組並將其第一個字符初始化爲‘\0’,將其以字符串的形式輸出至屏幕:
char bar[102];
bar[0] = '\0';
printf("%s")
我們知道,字符串以‘\0’爲結尾,所以上面這段僞代碼的結果是輸出一段空字符串。下面我再做點小修飾:
char bar[102];
int rate = 0;
bar[0] = '\0';
printf("%s")
while(rate <= 100)
{
printf("[%s] \n",bar);
sleep(3);
bar[rate] = '=';
rate++;
bar[rate] = '\0';
}
注意,此時我在字符串的後面加了一個‘\n’,進行了換行操作,所以你會看到下面的輸出結果:
[]
[=]
[==]
[===]
[====]
[=====]
[======]
…
…
如果我將‘\n’改成‘\r’呢?當第一次的字符串打印完畢後 ,不換行,執行”回車“操作,直接到這一行的開頭進行第二次打印,覆蓋之前的內容。。。就這樣一直到循環結束。
char bar[102];
int rate = 0;
bar[0] = '\0';
printf("%s")
while(rate <= 100)
{
printf("[%s] \n",bar);
sleep(3);
bar[rate] = '=';
rate++;
bar[rate] = '\0';
}
想象一下,輸出結果是什麼?
你會得到一個遞增的進度條?不!你什麼也得不到,請繼續往下看!
printf的緩衝區問題
在Linux下實現一個動態進度條只是上面的還不夠,在上面的代碼中有這兩行:
printf("[%s] \r",bar);
sleep(3);
這兩句實現出來就是先輸出,然後等待3s,再執行下面語句。如果你將這兩句單獨拿出來實現以下就會發現,它的實際執行結果是,先等待3s,再輸出。很奇怪是不是!這就涉及到了printf的IO緩衝區了。
在printf的實現中有一步調用write的操作。而write是一個系統調用,系統調用是軟中斷,頻繁調用會使內核頻繁陷入內核態,效率不是很高,所以printf的實現中在調用write之前,加了一個IO緩衝區。printf輸出數據的時候實際上是先往用戶空間的IO緩衝區寫,在滿足條件的情況下才會調用write並且刷新緩衝區,這樣會提高內核工作的效率。
滿足條件的情況有以下幾種:
- 緩衝區填滿;
- 寫入的字符中有‘\n’;
- 調用fflush函數手動刷新緩衝區;
- 調用scanf要從緩衝區中讀數據時,也會將緩衝區的數據刷新;
- printf語句生命結束時;
滿足上面任意一個條件,緩衝區都會進行刷新,然後將數據輸出至屏幕。緩衝區的大小一般爲1024bytes,我們進度條的實現最多輸出不到150個字符而且後面還不能加‘\n’換行符。所以我們只能在每次printf後面調用fflush手動刷新IO緩衝區,以達到在sleep之前輸出printf內容的目的。優化後,僞代碼爲:
char bar[102];
int rate = 0;
bar[0] = '\0';
printf("%s")
while(rate <= 100)
{
printf("[%s] \n",bar);
fflush(stdout);
sleep(3);
bar[rate] = '=';
rate++;
bar[rate] = '\0';
}
結果展示:
實現代碼
/*************************************************************************
> File Name: progbar.c
> Author:lzk
> Mail:939142928@qq,com
> Created Time: Sat 31 Dec 2016 10:45:12 PM CST
************************************************************************/
#include<stdio.h>
#include<unistd.h>
void progbar()
{
char bar[102];
char fun[5]="-//|\";
char*tmp = fun;
int rate = 0;
while(rate <= 100)
{
printf("[%-100s] [%d]% [%c] \r",bar,rate,*tmp);
tmp++;
if(*tmp == fun[4])
{
tmp = fun;
}
fflush(stdout);
usleep(50000);
bar[rate] = '=';
rate++;
bar[rate]='\0';
}
}
int main()
{
progbar();
return 0;
}