目錄
\r 、\n、fflush
倒計時
進度條
進度條進階版
結語
\r 、\n、fflush
首先我們先來認識這三個東西,這將會是我們接下來兩個小程序的重點之一
首先是我們的老演員\n,也就是回車加換行
這里面其實包含了兩個操作,一個叫做回車,一個叫做換行
而單純的回車就是指,我們現在的光標,回到當前行的最前面
而換行 + 回車就是來到下一行的最前面
而我們的 \r 就是單純的回車,回到當前行的最前面
但是我們還有一個緩沖區的概念
也就是,當我們使用了\n之后就會自動刷新緩沖區,但是\r不會
我們不想用\n是因為不想出現下面這樣的情況:
所以我們只想回到第一行然后從頭開始覆蓋式地進行寫入
但是如果是\r的話,就會出現一個情況就是,我們會等到程序結束之后才會把最后一次的結果打印出來,這時因為緩沖區不會因為\r刷新
所以我們這時候就需要fflush(stdout),這樣就能在不回車的前提下達到提前刷新緩沖區的效果了
至于stdout,這就是系統默認打開的標準輸出文件,其實我們這里可以粗淺的看作就是顯示器,我們需要刷新緩沖區才能讓顯示器上顯示結果
倒計時
至于倒計時,其實相當簡單,我們只需要知道,這是在不停地打印數字,并且會在同一個位置打印,這就需要用到回車(\r)回到最前面,然后覆蓋掉前面的結果以達到原地變化的效果
但是由于\r不會刷新緩沖區,所以我們就需要使用fflush刷新,這樣,我們的倒計時就寫好了
代碼如下:
void time_count_down(int total)
{int cnt = total;while(cnt >= 0){printf("倒計時: %2d\r", cnt);fflush(stdout);cnt--;usleep(300000);}printf("\n");
}
進度條
首先,我們需要知道的是,進度條的本質和倒計時是一樣的,就是\r移動光標,通過覆蓋式寫入達到變化的效果
我們先來看看成品的樣子:
分析一下,首先我們需要預留出一百個空間,表現出遞進的效果
這一步我們可以直接建一個數組,然后一個循環,每循環一次,就打印出當前數組里面的內容,并且在當前數組最后一個 “ = ” 后面再加一個等號,這樣,我們就達到了循環遞進的效果
但是,我們需要注意的是,這期間我們需要一直使用 \r 和 fflush,如下:
#define LENGTH 101
#define STYLE '='void ProcessBar()
{char bar[LENGTH];memset(bar, '\0', sizeof bar);int cnt = 0;while(cnt <= 100){printf("[%-100s]\r", bar);fflush(stdout);bar[cnt++] = STYLE;usleep(10000);}printf("\n");
}
注意,上面的 “%-100d” 中,100就代表默認留出100個位置,-100則代表左對齊
接著,我們可以看到,進度條后面還有兩個東西:
這兩個,一個是實時顯示當前加載進度的數字顯示,還有一個是想通過 |/-\ 這四個字符,達成一條線在不停旋轉的動態效果,也就是在提醒用戶,可能用戶看到進度條不動了以為是網卡了,但其實還在下載,只是進度比較慢,暫時卡住了而已,動態的話就是為了避免這個問題
首先來解決第一個
這個其實我們只需要把數字填進去就可以了,只是后面的%需要寫兩個而已,寫一個顯示不出來
而最后一個就是,我們先寫一個數組:
const char* label = "|/-\\";
我們只需要不停的從左往右選擇即可,但是由于會越界,所以我們就需要%一個數組大小,這樣才不會越界
而其中 \\ 有兩個是因為只寫一個不顯示
比如,我現在數組大小是4,如果當前數字大小是4,4%4 = 0,那么就會直接回到第一個數組下標位置,5%4就是1,代表第二個位置,依此類推
總代碼如下:
#define LENGTH 101
#define STYLE '='
const char* label = "|/-\\";void ProcessBar()
{char bar[LENGTH];memset(bar, '\0', sizeof bar);int len = strlen(label);int cnt = 0;while(cnt <= 100){printf("[%-100s][%3d%%][%c]\r", bar, cnt, label[cnt % len]);fflush(stdout);bar[cnt++] = STYLE;usleep(10000);}printf("\n");
}
進度條進階版
現實中,我們的進度條都是搭配了任務一起的
比如我們可以寫一個下載的任務,影響下載的因素這里假設只有帶寬,他就相當于下載速度吧在這里
然后我們只需要提供一個文件總大小,模擬這個過程即可
代碼如下(模擬下載任務的代碼):
#define bandwidth 1024*1024*1.0;void download(double file_size)
{double current = 0.0;while(current <= file_size){//調用進度條ProcessBar(file_size, current);current += bandwidth;usleep(20000);}printf("\n");
}
這時候我們的進度條文件就需要更改一下了
由于會被頻繁調用,所以我們的進度條在這里,每一次調用都代表那一瞬間的狀態
所以我們在進度條那里的循環可以只是循環添加 =
接著,我們的進度這次就是按照百分比來算的了
所以進度條需要一個總的文件大小,以及現在文件加載到哪里了,這樣我們將兩個文件除出來之后再乘100,就是當前文件加載的百分比進度:
double rate = current*100.0/total;
接下來的都是小小修改一下而已,代碼如下:
#define LENGTH 101
#define STYLE '='
const char* label = "|/-\\";void ProcessBar(double total, double current)
{char bar[LENGTH];memset(bar, '\0', sizeof bar);int len = strlen(label);double rate = current*100.0/total;int loop_count = (int)rate;int cnt = 0;while(cnt <= loop_count){bar[cnt++] = STYLE;}printf("[%-100s][%.1lf%%][%c]\r", bar, rate, label[cnt % len]);fflush(stdout);
}
最后,我們還可以再來一個小優化
試想一下,我們以后如果有了圖形化界面,或者就是單純更好的進度條,對于這個下載任務而言,如果我們只是用一個函數指針的話,到時候我們只需要修改指針即可,這樣的話,修改起來就很方便,當我們有了更好的進度條的時候
如下,我們可以先在 .h 文件里面定義一個函數指針:
typedef void (*pb)(double, double);
接著,我們的下載任務里面,就可以直接加上這個參數了:
#define bandwidth 1024*1024*1.0;void download(double file_size, pb procbar)
{double current = 0.0;while(current <= file_size){procbar(file_size, current);current += bandwidth;usleep(20000);}printf("\n");
}int main()
{download(100.0*1024*1024, ProcessBar);return 0;
}
結語
這篇文章到這里就結束啦!!~( ̄▽ ̄)~*
如果覺得對你有幫助的,可以多多關注一下喔