在 C 語言重新整理 stdout 輸出流
本文將演示關於如何在 C 語言中重新整理 stdout
輸出流的多種方法。
在 C 語言中使用 fflush
函式來重新整理 stdout
輸出流
C 標準庫提供了一個 I/O
庫,即 stdio
,它基本上代表了在使用者空間中進行的 I/O 操作的緩衝版本,從而提高了常見用例的效能。一般來說,訪問檔案和對檔案進行操作是由作業系統服務提供的,因此,使用者最終需要系統呼叫,例如開啟一個檔案。頻繁的系統呼叫會使程式的執行速度變慢,因為它需要訪問作業系統的核心資料結構並來回傳輸控制。因此,在使用 stdio
函式呼叫時,C 庫會維護一些緩衝區來處理輸入/輸出操作。
如果使用者需要對核心緩衝區進行強制寫入,則需要對 fflush
函式提供的給定流進行重新整理。fflush
需要一個 FILE
的單引數,指向給定流的指標。請注意,fflush
對輸出流強制執行寫功能,同時丟棄輸入流的任何緩衝資料(有可尋檔案)。如果引數是 NULL
,它將重新整理所有開啟的輸出流。
注意,fflush
並不能確保寫入的資料被物理儲存,因為這需要核心緩衝區被重新整理(可以使用 fsync
呼叫來完成,請參見這裡)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char *username;
size_t len;
int lnmax = 256;
username = malloc(lnmax);
if (username == NULL) perror("malloc");
printf("Username: ");
fflush(stdout);
if (fgets(username, lnmax, stdin) == NULL) exit(EXIT_FAILURE);
printf("Your username is set to - %s", username);
exit(EXIT_SUCCESS);
}
輸出:
Username: tmp
Your username is set to - tmp
在 C 語言中使用 printf
函式演示 fflush
行為
請注意,有些流(如 stderr
)是沒有緩衝的。相反,隱含寫入 stdout
流的 printf
函式是有緩衝的,如果我們在下面執行每次迭代列印一個字元的無限迴圈,直到內部緩衝區滿了才會向流輸出內容。因此,下面的程式碼示例的結果是突發列印 bullet
字元。請注意,我們每次迭代都呼叫 usleep
函式,以減慢人眼的執行速度,以便於清晰地觀察到。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
while (1) {
printf(".");
usleep(1e3);
}
exit(EXIT_SUCCESS);
}
另外,如果我們用 fprintf
代替 printf
的呼叫,列印到 stderr
流,就會產生每秒鐘一個字元的迭代列印行為。再次強調,每次迭代的 1 秒延遲只是為了保證良好的演示效果。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
while (1) {
fprintf(stderr, ".");
sleep(1);
}
exit(EXIT_SUCCESS);
}
最後,如果我們需要在 stdout
流上模仿前面示例程式碼中看到的相同行為,我們可以在 printf
函式後新增 fflush
呼叫。這將迫使 C 庫緩衝區在每次迭代時寫入核心緩衝區,從而產生類似的行為。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
while (1) {
printf(".");
fflush(stdout);
sleep(1);
}
exit(EXIT_SUCCESS);
}