設計目標
- 處理任意長度行 (超過固定緩衝區)
- 正確處理
\n、\r\n、EOF 等換行風格
- 動態分配記憶體,呼叫者負責
free
- 錯誤/ EOF 語義清晰
完整實現碼
typedef int BOOL;
#define TRUE 1
#define FALSE 0
BOOL readLine(FILE *file, char **ppBuf) {
const int CHUNK = 128;
char chunk[CHUNK];
char *accum = NULL;
size_t accum_len = 0;
BOOL continue_read = TRUE;
do {
char *res = fgets(chunk, CHUNK, file);
if (res == NULL && !feof(file)) { // 真正錯誤
free(accum);
return FALSE;
}
if (res == NULL) break; // EOF
size_t n = strlen(chunk);
// 判斷是否讀完整行:緩衝區未滿 或 以 \n 結尾
if (n < CHUNK - 1 || chunk[n - 1] == '\n') {
if (n == 0) break; // 空行 (僅可能在 EOF 前)
continue_read = FALSE;
}
// 擴展累積緩衝區
char *tmp = realloc(accum, accum_len + n + 1);
if (!tmp) {
free(accum);
return FALSE;
}
accum = tmp;
memcpy(accum + accum_len, chunk, n);
accum_len += n;
accum[accum_len] = '\0';
} while (continue_read);
// 移除尾部換行符
if (accum_len > 0 && accum[accum_len - 1] == '\n') {
accum[--accum_len] = '\0';
}
if (accum_len > 0 && accum[accum_len - 1] == '\r') {
accum[--accum_len] = '\0';
}
if (accum_len == 0) {
free(accum);
*ppBuf = NULL;
return FALSE; // 空行或 EOF 無資料
}
*ppBuf = accum;
return TRUE;
}
關鍵設計細節
| 細節 | 處理方式 |
| 固定塊大小 | CHUNK=128,平衡系統呼叫次數與記憶體浪費 |
| 行結束判斷 | n < CHUNK-1 (fgets 未填滿緩衝區) 或 chunk[n-1]=='\n' |
| 記憶體增長 | realloc 精確增長至 accum_len + n + 1,避免指數級增長開銷 |
| 跨平台換行 | 依序移除 \n 再 \r,相容 Unix/Windows/Mac 格式 |
| 錯誤處理 | 任何分配失敗/讀取錯誤釋放已分配記憶體並回傳 FALSE |
| 擁有權移交 | 成功時 *ppBuf 指向累積緩衝區,呼叫者必須 free |
使用範例
FILE *fp = fopen("large_log.txt", "r");
char *line;
while (readLine(fp, &line)) {
printf("Line: %s\n", line);
free(line); // 重要!
}
fclose(fp);
與 getline() (POSIX.1-2008) 比較
| 特性 | 本實現 | getline() |
| 可移植性 | 純 C89,無相依 | 需 POSIX 系統 |
| 記憶體策略 | 精確增長 | 通常倍增策略 |
| 換行符保留 | 自動移除 | 保留在緩衝區 |
| 錯誤碼區分 | 簡單 TRUE/FALSE | 回傳讀取位元組數,-1 表錯誤/EOF |
現代 Linux/BSD 系統建議直接使用 getline() 或 getdelim();嵌入式/跨平台專案可採用本實現。
- 上一篇文章: C 語言巨集 #define 完整用法總結:從基礎替換到條件編譯
- 下一篇文章: Linux C 語言實現 ping (ICMPv4 Echo Request) 完整原始碼解析
Copyright ©
程式碼之家 All Rights Reserved