對文字檔某行進行替換,新內容比舊內容短。直接覆寫會導致行尾殘留舊字元,檔案長度不變。若不允許建立暫存檔 (rename 替換),如何原地截斷多餘字元?
| 方案 | 原理 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 記憶體映射 (mmap / MapViewOfFile) — 推薦 | 將檔案映射至進程位址空間,直接修改記憶體,`ftruncate`/`SetEndOfFile` 截斷 | 效能極佳 (OS 管理分頁);支援大檔案;原地修改;程式碼簡潔 | 需處理分頁對齊、權限、錯誤復原;Windows/Linux API 不同 | 大檔案、高效能需求、原地修改強制要求 |
| 讀入記憶體修改後整體寫回 | `fread` 全部載入 `char*`/`std::string`,修改字串,`fwrite` 覆寫、截斷 | 簡單直觀;跨平台;中小檔案極快 | 檔案超大時記憶體不足 | 檔案 < 可用記憶體 (如 < 100MB) |
| 雙緩衝就地重寫 (複雜、易錯) | 邊讀邊寫,維護讀/寫指標,最後 `ftruncate` | 常數記憶體 | 極易出錯 (覆寫未讀資料、編碼邊界);不推薦手寫 | 嚴格記憶體受限嵌入式環境 |
| 暫存檔 + rename (標準做法,題目禁用) | 寫新檔 → `rename()` 原子替換 | 安全、原子性、支援回滾、跨平台 | 需額外磁碟空間、雙倍 I/O | **絕大多數生產環境首選** |
#include
#include
#include
#include
int fd = open("data.txt", O_RDWR);
struct stat st; fstat(fd, &st);
size_t filesize = st.st_size;
// 映射整個檔案
char *map = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) { perror("mmap"); return -1; }
// 在映射記憶體中修改 (找到行、替換、計算新長度 new_len)
// ... 修改邏輯 ...
// 截斷檔案至新長度
ftruncate(fd, new_len);
// 解除映射
munmap(map, filesize);
close(fd);
HANDLE hFile = CreateFile("data.txt", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
LPVOID pMap = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
// 修改 pMap 指向的記憶體 ...
// 設定新檔案結尾
SetFilePointer(hFile, new_len, NULL, FILE_BEGIN);
SetEndOfFile(hFile);
UnmapViewOfFile(pMap);
CloseHandle(hMap);
CloseHandle(hFile);
**不允許暫存檔**的原地替換截斷,**記憶體映射 (mmap/MapViewOfFile) 是效能最好、實作最標準的方案**。中小檔案直接「全讀入記憶體 → 修改 → 全寫回 → 截斷」程式碼最簡單、可移植性最強。除非嵌入式極度受限,否則避免手寫就地重寫邏輯。