1. 前言
在程式設計中,檔案的讀寫是非常重要的操作之一。在 C 語言中,檔案操作的基本流程包括開啟檔案、寫入資料以及關閉檔案。本文將重點介紹在 C 語言中進行檔案寫入的基礎方法與具體範例。
檔案寫入可用於資料持久化以及與其他程式共享資料,因此是許多程式中必備的重要技能。此外,透過學習 C 語言中的檔案操作,也能更容易理解其他程式語言的檔案處理方式。透過本文,您將學習從基本寫入方法到進階錯誤處理的內容,並加深對檔案操作的理解。
下一章將從檔案的開啟與關閉操作,以及寫入模式的基礎知識開始說明。
2. 檔案寫入的基礎
在 C 語言中進行檔案寫入前,必須先開啟檔案,並指定開啟的目的。C 語言使用 fopen
函數來開啟檔案,並使用 fclose
函數關閉檔案。以下將說明檔案開啟與關閉的基本操作,以及各種寫入模式。
fopen
函數的使用方法
要開啟檔案可使用 fopen
函數。該函數接受檔案名稱與模式(檔案操作類型)作為引數。基本語法如下:
FILE *fopen(const char *filename, const char *mode);
filename
:要開啟的檔案名稱(或路徑)mode
:開啟檔案的模式(寫入、讀取、附加等)
寫入模式的種類
檔案開啟模式有多種,以下介紹與寫入相關的幾種:
"w"
:只寫模式。若檔案已存在,內容會被清除;若不存在,則建立新檔。"a"
:附加模式。若檔案已存在,資料將附加到檔案末尾;若不存在,則建立新檔。"wb"
:二進位寫入模式。若檔案已存在,內容會被清除並以二進位方式寫入;若不存在,則建立新檔。
寫入範例
以下示範建立新檔並寫入資料的程式碼。若檔案已存在,內容將被清除,並以 "w"
模式寫入。
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // 以 "w" 模式開啟檔案
if (file == NULL) {
printf("無法開啟檔案。\n");
return 1;
}
fprintf(file, "您好,這是使用 C 語言寫入檔案的範例!\n");
fclose(file); // 關閉檔案
printf("檔案寫入完成。\n");
return 0;
}
在此範例中,fopen
用來建立名為 “example.txt” 的檔案,並使用 fprintf
將文字資料寫入。寫入完成後,務必使用 fclose
關閉檔案,否則可能導致資料未正確保存。
fclose
函數的重要性
fclose
必須在開啟檔案後呼叫,用於釋放系統資源並確保資料正確儲存。若未關閉檔案就結束程式,可能導致寫入中斷,因此養成使用 fclose
的習慣非常重要。
下一章將更詳細介紹文字檔案的寫入方法。
3. 寫入文字檔的方法
在 C 語言中,將資料寫入文字檔有三種方式:以字元為單位、以字串為單位,以及格式化資料的方式。針對不同需求,可使用對應的函數。本節將介紹 fputc
、fputs
、fprintf
這三種寫入方式。
fputc
函數 — 寫入單一字元
fputc
用於一次寫入一個字元,適合需要逐字元處理的情況。語法如下:
int fputc(int character, FILE *stream);
character
:要寫入的字元stream
:檔案指標
fputc
使用範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
printf("無法開啟檔案。\n");
return 1;
}
fputc('A', file); // 寫入 'A'
fputc('B', file); // 寫入 'B'
fputc('\n', file); // 寫入換行符
fclose(file);
printf("字元寫入完成。\n");
return 0;
}
此範例將字元 'A'
與 'B'
分別寫入檔案,適合處理小規模的字元資料。
fputs
函數 — 寫入整個字串
fputs
可一次寫入整段字串,效率較高。語法如下:
int fputs(const char *str, FILE *stream);
str
:要寫入的字串stream
:檔案指標
fputs
使用範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
printf("無法開啟檔案。\n");
return 1;
}
fputs("這是使用 fputs 寫入的範例。\n", file);
fclose(file);
printf("字串寫入完成。\n");
return 0;
}
此範例一次將完整字串寫入檔案,適合批量寫入文字資料。
fprintf
函數 — 寫入格式化資料
fprintf
是 printf
的檔案版本,可依格式化輸出不同型別的資料。
int fprintf(FILE *stream, const char *format, ...);
stream
:檔案指標format
:包含格式指定的字串
fprintf
使用範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
printf("無法開啟檔案。\n");
return 1;
}
int number = 123;
float decimal = 45.67;
fprintf(file, "整數: %d, 浮點數: %.2f\n", number, decimal);
fclose(file);
printf("格式化資料寫入完成。\n");
return 0;
}
此範例使用 fprintf
將整數與浮點數以指定格式寫入檔案,非常適合輸出報表或結構化資料。
小結
fputc
、fputs
、fprintf
都能用於文字檔的寫入,依需求選擇合適的函數可提高效率與靈活性。下一章將介紹如何將資料寫入二進位檔案。
4. 寫入二進位檔的方法
C 語言不僅能處理文字檔,還能直接寫入二進位檔,例如圖片、音訊或結構體資料。這可透過 fwrite
完成。
fwrite
函數的使用方法
fwrite
會將記憶體中的資料原封不動地寫入檔案,不會進行文字轉換。
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr
:資料的指標size
:單個元素的大小(位元組)count
:元素數量stream
:檔案指標
以二進位模式開啟檔案
寫入二進位檔時需使用 "wb"
或 "ab"
模式,確保資料不被轉換。
fwrite
寫入範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.bin", "wb");
if (file == NULL) {
printf("無法開啟檔案。\n");
return 1;
}
int data[] = {10, 20, 30, 40, 50};
size_t dataSize = sizeof(data) / sizeof(data[0]);
fwrite(data, sizeof(int), dataSize, file);
fclose(file);
printf("二進位資料寫入完成。\n");
return 0;
}
此範例將整數陣列直接以二進位格式寫入檔案,效率高且適合大量資料處理。
二進位檔的注意事項
- 資料相容性:二進位資料在不同系統間可能無法正確讀取。
- 位元組序(Endianness):不同架構的位元組序不同,跨平台需進行轉換。
- 換行處理:二進位模式下換行符不會自動轉換,資料將原封不動保存。
小結
二進位檔寫入適用於需要完整保留原始資料的情況,例如圖片或結構體。使用 fwrite
可有效率地保存資料。下一章將介紹檔案操作中的錯誤處理方法。
5. 錯誤處理(Error Handling)
在進行檔案操作時,可能因檔案不存在、權限不足等原因導致錯誤。妥善處理這些錯誤,可避免程式出現異常行為並提升可靠性。本節將介紹在 C 語言中進行檔案操作時的錯誤處理方法。
檔案開啟時的錯誤檢查
當檔案不存在或權限不足時,fopen
會回傳 NULL
。透過檢查此值即可判斷檔案是否開啟成功。
檔案開啟錯誤檢查範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("無法開啟檔案");
return 1;
}
// 檔案處理程式碼
fclose(file);
return 0;
}
此範例中,當 fopen
失敗時,使用 perror
輸出錯誤訊息,可快速得知錯誤原因。
perror
與 strerror
顯示錯誤訊息
perror
:將自訂訊息與系統錯誤原因一併輸出到標準錯誤輸出(stderr
)。strerror
:接收錯誤代碼並回傳對應的錯誤訊息字串。
strerror
使用範例
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
printf("錯誤: %s\n", strerror(errno));
return 1;
}
fclose(file);
return 0;
}
此範例使用 errno
取得最近一次錯誤的代碼,並透過 strerror
轉換為可讀訊息。
偵測與處理寫入錯誤
在寫入檔案時,也可能發生錯誤,可用 ferror
檢查檔案流是否有錯誤。
寫入錯誤檢查範例
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w");
if (file == NULL) {
perror("無法開啟檔案");
return 1;
}
if (fprintf(file, "寫入資料") < 0) {
perror("寫入錯誤發生");
fclose(file);
return 1;
}
fclose(file);
printf("寫入完成。\n");
return 0;
}
此範例檢查 fprintf
的回傳值,若小於 0 代表寫入失敗,並輸出錯誤訊息。
小結
在檔案操作中實作錯誤處理,可提升程式的安全性與穩定性。包括檢查檔案開啟結果、使用錯誤訊息函數以及檢測寫入錯誤等,都是必備的處理方式。
6. 應用範例
掌握檔案寫入基礎後,我們可以將其應用在實際開發中,例如紀錄日誌、產生設定檔、資料序列化與反序列化等。
寫入日誌檔
日誌檔可用來記錄程式執行狀態與錯誤,方便除錯與追蹤。
日誌寫入範例
#include <stdio.h>
#include <time.h>
void log_message(const char *message) {
FILE *file = fopen("log.txt", "a");
if (file == NULL) {
perror("無法開啟日誌檔");
return;
}
time_t now = time(NULL);
struct tm *t = localtime(&now);
fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec, message);
fclose(file);
}
int main() {
log_message("程式已啟動。");
log_message("發生錯誤。");
return 0;
}
此範例使用附加模式 "a"
開啟檔案,保留舊資料並新增新的日誌內容。
產生設定檔
設定檔可保存應用程式的配置,方便啟動時讀取。
設定檔寫入範例
#include <stdio.h>
void save_settings(const char *filename, int volume, int brightness) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
perror("無法開啟設定檔");
return;
}
fprintf(file, "volume=%d\n", volume);
fprintf(file, "brightness=%d\n", brightness);
fclose(file);
}
int main() {
save_settings("settings.conf", 75, 50);
printf("設定檔已儲存。\n");
return 0;
}
設定檔以「鍵=值」的格式保存,方便讀取與修改。
資料序列化與反序列化
序列化可將資料結構直接儲存到檔案中,之後再讀取回記憶體。
結構體序列化範例
#include <stdio.h>
typedef struct {
int id;
char name[50];
float score;
} Student;
void save_student(const char *filename, Student *student) {
FILE *file = fopen(filename, "wb");
if (file == NULL) {
perror("無法開啟檔案");
return;
}
fwrite(student, sizeof(Student), 1, file);
fclose(file);
}
void load_student(const char *filename, Student *student) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("無法開啟檔案");
return;
}
fread(student, sizeof(Student), 1, file);
fclose(file);
}
int main() {
Student s1 = {1, "佐藤太郎", 89.5};
save_student("student.dat", &s1);
Student s2;
load_student("student.dat", &s2);
printf("ID: %d, 姓名: %s, 分數: %.2f\n", s2.id, s2.name, s2.score);
return 0;
}
此範例將結構體以二進位方式儲存與讀取,確保資料格式完全一致。
小結
透過日誌、設定檔與資料序列化的範例,可理解檔案寫入在實際專案中的應用方式。
7. 常見問題(FAQ)
以下整理了初學者在使用 C 語言進行檔案寫入時常見的問題與解決方法,幫助您快速排解檔案操作上的疑慮。
檔案無法開啟時怎麼辦?
Q: fopen
開檔失敗,該怎麼處理?
A: 若 fopen
回傳 NULL
,請檢查以下幾點:
- 檔案路徑是否正確:確認檔案實際位置與程式使用的路徑一致。
- 存取權限:確定檔案具有讀寫權限。
- 磁碟空間:若磁碟空間不足,可能無法建立新檔。
- 使用錯誤輸出:透過
perror
或strerror
輸出錯誤訊息以方便除錯。
寫入後檔案內容沒有更新
Q: 資料已寫入,但檔案內容沒有改變?
A: 可能原因如下:
- 忘記呼叫
fclose
:檔案未關閉可能導致資料留在緩衝區未寫入磁碟。 - 需要立即更新:可呼叫
fflush(file);
強制將緩衝資料寫入。 - 作業系統快取延遲:有些系統會延遲寫入,檢查檔案前可重新整理檔案檢視器。
二進位檔與文字檔有什麼不同?
Q: 兩者差在哪裡?
A:
- 文字檔:以純文字儲存,系統可能會轉換換行符號(Windows 使用
\r\n
,UNIX/Linux 使用\n
)。 - 二進位檔:原始位元資料直接儲存,不做任何轉換,適合圖片、音訊、結構體等。
寫入時出現錯誤
Q: 使用 fprintf
或 fwrite
時發生錯誤該怎麼辦?
A:
- 使用
ferror
檢查檔案流是否發生錯誤。 - 透過
perror
輸出詳細錯誤訊息。 - 確認磁碟空間是否足夠。
- 檢查檔案開啟模式是否允許寫入。
二進位檔跨平台讀取錯亂
Q: 讀取不同系統寫入的二進位檔出現亂碼或數值錯誤?
A: 這可能是位元組序(Endianness)不同造成的。
- 在儲存時使用
htons
、htonl
等函數統一成網路位元組序。 - 檔案中保存位元組序資訊,讀取時依據該資訊轉換。
小結
透過檢查權限、正確關閉檔案、使用錯誤處理函數與注意位元組序,可解決大部分檔案寫入問題。
8. 總結
本文由淺入深介紹了 C 語言的檔案寫入,包括:
- 使用
fopen
與fclose
開啟/關閉檔案的基礎操作。 - 以
fputc
、fputs
、fprintf
寫入文字檔。 - 使用
fwrite
寫入二進位資料及注意事項。 - 檔案操作錯誤處理技巧(
perror
、strerror
、ferror
)。 - 應用範例:日誌、設定檔、資料序列化。
- 常見問題與解決方法。
掌握這些技巧,不僅能在 C 語言中靈活操作檔案,也能更容易遷移到其他語言的檔案處理開發中。
建議讀者在實作中多嘗試不同寫入模式與錯誤處理方式,以提升程式的穩定性與可維護性。