1. 什麼是 fprintf 函數
fprintf 的基本概要
fprintf
函數是 C 語言中常用的標準輸入輸出函數之一。它的主要功能是「以格式化的方式輸出字串」。透過 fprintf
,可以依照指定的格式,將資料整理後寫入到目標輸出位置。
一般來說,fprintf
常被用在以下情境:
- 建立日誌檔案:記錄程式的執行歷程或錯誤資訊。
- 儲存格式化資料:將數值或字串以固定格式寫入檔案。
- 輸出除錯資訊:在開發過程中輸出資料以確認程式運作狀態。
fprintf 的基本語法
int fprintf(FILE *stream, const char *format, ...);
語法各部分說明
FILE *stream
:指定輸出位置,例如標準輸出(stdout
)或用fopen
開啟的檔案。const char *format
:定義輸出的格式字串,語法與printf
相同。...
:可變長參數,用來傳入實際要輸出的資料。
回傳值為成功寫入的字元數(正整數)。若發生錯誤則回傳 -1
。
與其他函數的比較
與 fprintf
類似的函數有 printf
與 sprintf
。以下整理它們的差異:
與 printf 的差異
printf
用於將資料輸出到標準輸出(通常是終端機)。而 fprintf
可以指定輸出目的地,因此更具彈性。
範例:使用 printf
printf("Hello, World!\n");
這會固定輸出到終端機。
範例:使用 fprintf
FILE *file = fopen("output.txt", "w");
fprintf(file, "Hello, World!\n");
fclose(file);
在這裡,輸出內容會被寫入指定的檔案(output.txt
)。
與 sprintf 的差異
sprintf
的輸出目標是「字串緩衝區」,也就是記憶體中的字串,而不是檔案。
範例:使用 sprintf
char buffer[50];
sprintf(buffer, "The result is %d", 42);
此時字串 "The result is 42"
會被寫入 buffer
。
總結
fprintf
可靈活指定輸出位置,如檔案或標準輸出,是非常實用的函數。- 搭配其他輸出函數(如
printf
與sprintf
)使用,可以提升程式的效率與可讀性。
2. fprintf 的基本用法
語法與基本參數說明
fprintf
函數是一個能以格式化方式輸出資料的彈性工具。它的基本語法如下:
int fprintf(FILE *stream, const char *format, ...);
以下說明各參數的細節:
- FILE *stream
- 指定輸出目標。
- 常見選項:
- 標準輸出(
stdout
) - 標準錯誤輸出(
stderr
) - 檔案(由
fopen
函數開啟的檔案)
- 標準輸出(
- const char *format
- 定義輸出格式。
- 可使用格式指定符號來輸出字串、整數、浮點數等(例:
%s
,%d
,%f
)。
- 可變長參數(…)
- 對應格式指定符號所需的實際資料。
- 例如:格式
"Name: %s, Age: %d"
,就需要傳入名字與年齡。
回傳值為成功寫入的字元數(正整數)。若發生錯誤,則回傳 -1
。
基本程式範例
以下示範 fprintf
的簡單用法。
輸出到標準輸出
將字串以格式化方式輸出到標準輸出(stdout
)。
#include <stdio.h>
int main() {
fprintf(stdout, "Hello, %s! You have %d new messages.\n", "Alice", 5);
return 0;
}
輸出結果:
Hello, Alice! You have 5 new messages.
在這個範例中,明確指定了標準輸出 stdout
。
輸出到檔案
使用 fprintf
將資料寫入檔案。
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w"); // 以「寫入模式」開啟檔案
if (file == NULL) {
fprintf(stderr, "Error opening file.\n");
return 1;
}
fprintf(file, "Name: %s, Age: %d\n", "Bob", 30);
fclose(file); // 關閉檔案
return 0;
}
output.txt 的內容:
Name: Bob, Age: 30
格式指定符的基礎
fprintf
允許使用格式指定符來靈活控制輸出格式。以下是基本的指定符範例:
指定符 | 說明 | 範例 |
---|---|---|
%d | 十進位整數 | 42 |
%f | 浮點數 | 3.141593 |
%s | 字串 | "Hello" |
%c | 單一字元 | 'A' |
%x | 十六進位(小寫) | 0x2a |
%o | 八進位 | 052 |
範例:
fprintf(stdout, "Integer: %d, Float: %.2f, String: %s\n", 10, 3.14, "Test");
輸出結果:
Integer: 10, Float: 3.14, String: Test
3. fprintf 的格式指定應用
欄寬(Minimum Width)
指定欄寬時,若輸出字數不足,會以空白補齊。
範例:
fprintf(stdout, "|%10s|\n", "Hello");
fprintf(stdout, "|%10d|\n", 123);
輸出結果:
| Hello|
| 123|
這裡的欄寬被設定為 10。當字元數不足時,會在左側補上空白。
精度(Precision)
精度的意義依據不同型別而有所差異:
- 字串(%s):輸出的最大字元數。
- 浮點數(%f, %e, %g):小數點後的位數。
範例:
fprintf(stdout, "%.3f\n", 3.141592); // 浮點數精度
fprintf(stdout, "%.5s\n", "Hello, World!"); // 字串最大長度
輸出結果:
3.142
Hello
旗標(Flags)
旗標可以控制輸出的對齊或格式。
旗標 | 說明 | 範例 |
---|---|---|
- | 靠左對齊(預設為靠右) | |%-10s| → |Hello | |
+ | 數字總是顯示正負號(正數也加 + ) | %+d → +42 |
0 | 補零(需搭配欄寬) | %05d → 00042 |
# | 特殊型別格式(十六進位或八進位) | %#x → 0x2a |
(空白) | 在正數前插入空格 | % d → 42 |
範例:
fprintf(stdout, "|%-10s|%+05d|%#x|\n", "Left", 42, 42);
輸出結果:
|Left |+0042|0x2a|
實際應用範例
結合欄寬、精度與旗標,可以輸出格式化的表格資料。
表格形式輸出
以下示範將學生的成績以表格方式輸出:
#include <stdio.h>
int main() {
fprintf(stdout, "|%-10s|%5s|%5s|%5s|\n", "Name", "Math", "Eng", "Sci");
fprintf(stdout, "|%-10s|%5d|%5d|%5d|\n", "Alice", 95, 88, 92);
fprintf(stdout, "|%-10s|%5d|%5d|%5d|\n", "Bob", 82, 79, 85);
return 0;
}
輸出結果:
|Name | Math| Eng| Sci|
|Alice | 95| 88| 92|
|Bob | 82| 79| 85|
數值資料的格式化
將數值以格式化方式輸出,讓顯示更整齊。
範例:
fprintf(stdout, "Price: $%8.2f\n", 1234.5);
fprintf(stdout, "Discount: %06d%%\n", 25);
輸出結果:
Price: $ 1234.50
Discount: 000025%
注意事項
- 不正確的格式指定符
- 若格式指定符與資料型別不相符,可能導致非預期輸出或錯誤。
- 例:將字串傳給
%d
會造成未定義行為。
- 欄寬與精度設定
- 若指定過大的欄寬,可能導致輸出過於冗長並浪費資源。
總結
- 透過欄寬、精度與旗標,可以細緻控制
fprintf
的輸出。 - 格式化表格或數值輸出能讓結果更易讀。
- 確保格式指定符與資料型別一致,才能保證安全輸出。
4. 檔案操作與 fprintf
開啟檔案的方法(fopen)
在使用 fprintf
將資料寫入檔案之前,必須先開啟檔案。在 C 語言中,可以使用 fopen
函數來開啟檔案。
fopen 的基本語法
FILE *fopen(const char *filename, const char *mode);
參數說明
filename
:要開啟的檔案名稱(含路徑)。mode
:檔案開啟模式。"r"
:唯讀模式"w"
:寫入模式(若檔案已存在則覆蓋)"a"
:附加模式(將內容加到檔案末尾)"rb"
/"wb"
/"ab"
:以二進位模式操作(分別對應r
/w
/a
)
回傳值
- 成功:回傳
FILE
型態指標。 - 失敗:回傳
NULL
。
fopen 使用範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
fprintf(file, "Hello, World!\n");
fclose(file);
return 0;
}
這段程式會開啟 example.txt
,寫入內容後再關閉。
使用 fprintf 寫入檔案
透過 fprintf
可以將格式化的資料寫入已開啟的檔案。以下示範幾種情境:
基本檔案寫入
#include <stdio.h>
int main() {
FILE *file = fopen("data.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
fprintf(file, "Name: %s, Age: %d\n", "Alice", 25);
fprintf(file, "Name: %s, Age: %d\n", "Bob", 30);
fclose(file);
return 0;
}
data.txt 的內容:
Name: Alice, Age: 25
Name: Bob, Age: 30
建立 CSV 檔案
以下範例將資料以 CSV(逗號分隔值)格式儲存:
#include <stdio.h>
int main() {
FILE *file = fopen("students.csv", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
// 標題列
fprintf(file, "Name,Math,English,Science\n");
// 資料列
fprintf(file, "Alice,95,88,92\n");
fprintf(file, "Bob,82,79,85\n");
fclose(file);
return 0;
}
students.csv 的內容:
Name,Math,English,Science
Alice,95,88,92
Bob,82,79,85
關閉檔案(fclose)
完成檔案操作後,必須使用 fclose
來關閉檔案,否則可能出現以下問題:
- 資料未完整寫入檔案。
- 系統資源浪費。
fclose 的基本語法
int fclose(FILE *stream);
回傳值
- 成功:回傳
0
。 - 失敗:回傳 EOF。
fclose 範例
FILE *file = fopen("example.txt", "w");
if (file != NULL) {
fprintf(file, "This is a test.\n");
fclose(file);
}
安全的檔案操作建議
- 檢查檔案指標
- 確認
fopen
的回傳值是否為NULL
。
- 避免忘記關檔
- 開啟檔案後務必呼叫
fclose
。
- 錯誤處理
- 偵測並處理檔案操作中的錯誤,例如磁碟空間不足或權限錯誤。
總結
- 使用
fprintf
時,應先以fopen
開啟檔案並在結束時以fclose
關閉。 - 正確處理檔案模式與錯誤處理,可確保操作安全有效。
- 應用範例:CSV 資料儲存、日誌檔紀錄。
5. 錯誤處理(Error Handling)
利用 fprintf 的回傳值進行錯誤檢測
透過檢查 fprintf
的回傳值,可以判斷寫入是否成功。
回傳值規則
- 成功寫入:回傳已寫入的字元數(正整數)。
- 寫入失敗:回傳
-1
。
基本錯誤檢查範例
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
int result = fprintf(file, "Hello, World!\n");
if (result < 0) {
fprintf(stderr, "Error: Failed to write to file.\n");
}
fclose(file);
return 0;
}
這個程式會檢查 fprintf
的回傳值,若寫入失敗則輸出錯誤訊息。
使用標準錯誤輸出(stderr)
stderr
是專門用來輸出錯誤或警告的標準輸出流。利用 stderr
可以將錯誤訊息與一般輸出(stdout
)分開。
範例:輸出錯誤訊息到 stderr
#include <stdio.h>
int main() {
FILE *file = fopen("nonexistent_directory/output.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Unable to open file. Check the directory path.\n");
return 1;
}
fclose(file);
return 0;
}
輸出結果(錯誤時):
Error: Unable to open file. Check the directory path.
實用錯誤處理範例
以下展示一個處理檔案寫入與關閉錯誤的完整範例:
範例:檔案寫入與關閉的錯誤檢查
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
// 嘗試寫入
if (fprintf(file, "Logging data: %d\n", 42) < 0) {
fprintf(stderr, "Error: Failed to write to file.\n");
fclose(file);
return 1;
}
// 檢查檔案是否正確關閉
if (fclose(file) != 0) {
fprintf(stderr, "Error: Failed to close the file.\n");
return 1;
}
printf("File operation completed successfully.\n");
return 0;
}
要點:
- 在檔案開啟、寫入、關閉等步驟均檢查錯誤。
- 錯誤發生時輸出訊息並結束程式。
常見錯誤與解決方式
1. 無法開啟檔案
可能原因:
- 檔案不存在。
- 目錄路徑錯誤。
- 權限不足。
解決方式:
- 確認檔案路徑是否正確。
- 檢查並調整檔案權限。
- 檢查
fopen
的回傳值。
2. 寫入失敗
可能原因:
- 磁碟空間不足。
- 檔案以唯讀模式開啟。
解決方式:
- 確認開啟模式(建議
"w"
或"a"
)。 - 檢查磁碟剩餘空間。
3. 關閉檔案失敗
可能原因:
- 系統資源不足。
- 硬體異常。
解決方式:
- 檢查
fclose
的回傳值。 - 僅開啟必要的檔案以避免資源耗盡。
總結
- 透過檢查
fprintf
的回傳值,可以偵測寫入錯誤。 - 利用
stderr
可以將錯誤與一般輸出分開。 - 妥善實作錯誤處理,可提升程式的穩定性與可靠性。
6. 應用範例
自動生成日誌檔
日誌檔常用來記錄程式執行情況與錯誤資訊。以下範例示範如何輸出包含日期時間的日誌。
範例:輸出含日期時間的日誌
#include <stdio.h>
#include <time.h>
int main() {
FILE *logFile = fopen("log.txt", "a"); // 以附加模式開啟
if (logFile == NULL) {
fprintf(stderr, "Error: Could not open log file.\n");
return 1;
}
time_t now = time(NULL);
struct tm *localTime = localtime(&now);
fprintf(logFile, "[%04d-%02d-%02d %02d:%02d:%02d] Program started\n",
localTime->tm_year + 1900, localTime->tm_mon + 1, localTime->tm_mday,
localTime->tm_hour, localTime->tm_min, localTime->tm_sec);
fclose(logFile);
return 0;
}
log.txt 的內容:
[2025-01-19 15:45:30] Program started
要點
- 使用
time.h
取得當前日期與時間。 - 使用附加模式(
"a"
)在檔案尾端寫入新資料。
表格形式輸出資料
以下範例示範如何輸出整齊的表格,適合用於報表或匯出資料。
範例:輸出學生成績表
#include <stdio.h>
int main() {
FILE *file = fopen("report.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
fprintf(file, "|%-10s|%6s|%6s|%6s|\n", "Name", "Math", "Eng", "Sci");
fprintf(file, "|%-10s|%6d|%6d|%6d|\n", "Alice", 90, 85, 88);
fprintf(file, "|%-10s|%6d|%6d|%6d|\n", "Bob", 78, 82, 80);
fclose(file);
return 0;
}
report.txt 的內容:
|Name | Math| Eng| Sci|
|Alice | 90| 85| 88|
|Bob | 78| 82| 80|
要點
- 使用
%-10s
控制字串靠左,%6d
控制數字靠右。 - 能輸出整齊一致的表格格式。
儲存資料為 CSV 檔
CSV(Comma-Separated Values)常用於資料儲存與跨程式交換。
範例:輸出 CSV 格式資料
#include <stdio.h>
int main() {
FILE *file = fopen("data.csv", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
// 標題列
fprintf(file, "Name,Math,English,Science\n");
// 資料列
fprintf(file, "Alice,90,85,88\n");
fprintf(file, "Bob,78,82,80\n");
fclose(file);
return 0;
}
data.csv 的內容:
Name,Math,English,Science
Alice,90,85,88
Bob,78,82,80
要點
- 使用逗號(
,
)分隔欄位,便於 Excel、Python 等工具讀取。
記錄除錯資訊
透過輸出除錯用的日誌,可以更容易追蹤程式運作。
範例:記錄執行時變數
#include <stdio.h>
int main() {
FILE *debugFile = fopen("debug.log", "w");
if (debugFile == NULL) {
fprintf(stderr, "Error: Could not open debug log file.\n");
return 1;
}
int x = 42;
fprintf(debugFile, "Debug: Variable x = %d\n", x);
fclose(debugFile);
return 0;
}
debug.log 的內容:
Debug: Variable x = 42
要點
- 將變數值輸出到檔案,有助於追蹤與分析程式狀態。
總結
- 利用
fprintf
可以靈活輸出日誌檔、表格、CSV 等格式。 - 實務上,最常用的是「日期時間日誌」與「CSV 格式」。
- 透過這些應用,可以更高效地管理與分析程式資料。

7. 常見問題(FAQ)
1. fprintf 和 printf 有什麼差別?
回答
printf
:- 將資料輸出到標準輸出(通常是終端機)。
- 無法改變輸出目的地。
fprintf
:- 可以自由指定輸出目的地(如檔案、標準輸出、標準錯誤輸出)。
- 提供更高的彈性與控制能力。
範例
#include <stdio.h>
int main() {
printf("This is printed to the console.\n"); // 永遠輸出到終端機
FILE *file = fopen("output.txt", "w");
if (file != NULL) {
fprintf(file, "This is written to a file.\n"); // 輸出到檔案
fclose(file);
}
return 0;
}
2. 如何正確輸出中文(或日文)到檔案?
回答
- 要正確輸出中文(或日文),需要注意以下兩點:
- 字元編碼:
- 依照環境設定,選擇正確的編碼(例如:UTF-8、Big5、Shift-JIS)。
- 檔案編碼一致:
- 寫入檔案時的編碼需與系統或應用程式讀取設定一致。
範例:使用 UTF-8 輸出中文
#include <stdio.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, ""); // 設定地區與語言環境
FILE *file = fopen("chinese.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
fprintf(file, "你好,世界!\n");
fclose(file);
return 0;
}
注意:
- 在某些系統上,可能需要明確指定檔案的編碼格式以避免亂碼。
3. 為什麼 fprintf 會發生錯誤?
回答
- 常見原因如下:
- 檔案無法開啟:
- 檔案路徑錯誤。
- 權限不足。
- 磁碟空間不足:
- 在寫入過程中空間不足,導致失敗。
- 格式指定符不正確:
- 資料型別與格式符號不相符。
範例:格式符號不一致
#include <stdio.h>
int main() {
FILE *file = fopen("error.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
// 這裡用 %s 但傳入整數,會造成未定義行為
fprintf(file, "%s", 42);
fclose(file);
return 0;
}
解決方式:
- 確認格式符號與資料型別是否對應正確(例如:
%d
對應整數,%s
對應字串)。
4. fprintf 的緩衝機制會造成影響嗎?
回答
- 緩衝機制:
- 輸出內容會先進入緩衝區,直到緩衝區滿、或呼叫
fclose
/fflush
才會寫入檔案。 - 可能的問題:
- 若程式意外中止,緩衝區資料可能未被寫入檔案。
解決方式
- 使用
fflush
:手動刷新緩衝區。
範例:使用 fflush
#include <stdio.h>
int main() {
FILE *file = fopen("buffered_output.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
fprintf(file, "Buffered data.\n");
fflush(file); // 強制寫入檔案
fclose(file);
return 0;
}
5. 為什麼檔案輸出會中途被截斷?
回答
- 可能原因:
- 檔案未正確關閉:
- 緩衝區未被刷新,導致部分資料遺失。
- 磁碟空間不足。
範例:正確關閉檔案
#include <stdio.h>
int main() {
FILE *file = fopen("partial_output.txt", "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file.\n");
return 1;
}
fprintf(file, "This is complete data.\n");
fclose(file); // 不可忘記關閉
return 0;
}
解決方式:
- 務必呼叫
fclose
,確保輸出完整。 - 若有錯誤,應檢查回傳值以便處理。
總結
fprintf
雖然功能靈活,但需要正確的錯誤處理與字元編碼設定。- 掌握 FAQ 中的要點,可以避免大多數常見問題。
8. 同時輸出多個檔案
利用 fprintf
,可以同時將資料寫入多個檔案。在實務應用中,這種方法常用於記錄多份日誌或產生多個輸出檔案。
同時處理多個檔案的基本結構
在 C 語言中,可以使用多個 FILE
指標,同時操作不同檔案。必須確保對每個檔案正確執行 fopen
、fprintf
與 fclose
。
範例:同時輸出到兩個檔案
#include <stdio.h>
int main() {
// 開啟兩個檔案
FILE *file1 = fopen("output1.txt", "w");
FILE *file2 = fopen("output2.txt", "w");
if (file1 == NULL || file2 == NULL) {
fprintf(stderr, "Error: Could not open one of the files.\n");
if (file1) fclose(file1);
if (file2) fclose(file2);
return 1;
}
// 寫入檔案 1
fprintf(file1, "This is the first file.\n");
// 寫入檔案 2
fprintf(file2, "This is the second file.\n");
// 關閉檔案
fclose(file1);
fclose(file2);
printf("Data written to both files successfully.\n");
return 0;
}
output1.txt 的內容:
This is the first file.
output2.txt 的內容:
This is the second file.
要點
- 錯誤檢查:需確認每個
fopen
是否成功。 - 資源釋放:所有開啟的檔案必須以
fclose
關閉。
動態檔案操作
也可以動態生成檔名,將資料分別寫入多個檔案。
範例:動態生成檔名並輸出
#include <stdio.h>
int main() {
char filename[20];
for (int i = 1; i <= 3; i++) {
// 動態產生檔名
sprintf(filename, "file%d.txt", i);
FILE *file = fopen(filename, "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open %s\n", filename);
continue;
}
fprintf(file, "This is file number %d\n", i);
fclose(file);
}
printf("Data written to files successfully.\n");
return 0;
}
產生的檔案內容:
file1.txt
:This is file number 1
file2.txt
:This is file number 2
file3.txt
:This is file number 3
要點
- 利用
sprintf
產生檔名。 - 若開檔失敗,透過
continue
跳過並處理下一個檔案。
多檔案並行寫入
在需要同時寫入大量資料時,可以利用多執行緒(threads)來進行平行操作。
範例:使用 POSIX Threads 並行寫入
#include <stdio.h>
#include <pthread.h>
void *write_to_file(void *arg) {
char *filename = (char *)arg;
FILE *file = fopen(filename, "w");
if (file == NULL) {
fprintf(stderr, "Error: Could not open %s\n", filename);
return NULL;
}
fprintf(file, "Data written to %s\n", filename);
fclose(file);
return NULL;
}
int main() {
pthread_t threads[3];
char *filenames[] = {"thread1.txt", "thread2.txt", "thread3.txt"};
for (int i = 0; i < 3; i++) {
pthread_create(&threads[i], NULL, write_to_file, filenames[i]);
}
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
printf("Data written to all files in parallel.\n");
return 0;
}
產生的檔案:
thread1.txt
:Data written to thread1.txt
thread2.txt
:Data written to thread2.txt
thread3.txt
:Data written to thread3.txt
要點
- 透過執行緒可同時寫入多個檔案,提高效能。
- 使用
pthread_join
確保所有執行緒完成。
總結
fprintf
可以同時輸出到多個檔案。- 結合動態檔名與多執行緒,可提升彈性與效能。
- 務必做好錯誤處理與檔案關閉,避免資源洩漏。