檔案 I/O 基礎:質數篩選與格式化輸出
結合質數判斷演算法與檔案操作,將 1000 以內所有質數寫入文字檔,同時在螢幕顯示,每行 10 個數字對齊排版。
完整程式碼
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define LIMIT 1000
#define FILEPATH "primes.txt"
#define PER_LINE 10
bool isPrime(int n) {
if (n < 2) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) return false;
}
return true;
}
int main() {
FILE *fp = fopen(FILEPATH, "w");
if (!fp) {
perror("無法開啟檔案");
return 1;
}
int count = 0;
printf("1000 以內的質數:\n");
for (int n = 2; n < LIMIT; n++) {
if (isPrime(n)) {
// 螢幕輸出:寬度 5 右對齊
printf("%5d ", n);
// 檔案輸出:寬度 4 右對齊
fprintf(fp, "%4d ", n);
count++;
// 每行換行
if (count % PER_LINE == 0) {
printf("\n");
fprintf(fp, "\n");
}
}
}
// 最後一行若未滿 PER_LINE,補換行
if (count % PER_LINE != 0) {
printf("\n");
fprintf(fp, "\n");
}
fclose(fp);
printf("\n總共找到 %d 個質數,已寫入 %s\n", count, FILEPATH);
return 0;
}
關鍵函數說明
| 函數 | 用途 | 範例 |
| fopen(path, "w") | 開啟檔案寫入 (截斷/建立) | fp = fopen("primes.txt", "w") |
| fprintf(fp, fmt, ...) | 格式化寫入檔案 | fprintf(fp, "%d ", n) |
| fclose(fp) | 關閉檔案,刷新緩衝區 | fclose(fp) |
| perror(msg) | 印出系統錯誤訊息 | perror("開檔失敗") |
質數判斷優化
- 試除法優化:只檢查到
sqrt(n),且跳過偶數
- 埃拉托斯特尼篩法:O(n log log n) 批量篩選,更適合大範圍
篩法實作 (補充)
void sieve(int limit, FILE *fp) {
bool *isPrime = calloc(limit + 1, sizeof(bool));
for (int i = 2; i <= limit; i++) isPrime[i] = true;
for (int i = 2; i * i <= limit; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= limit; j += i)
isPrime[j] = false;
}
}
int count = 0;
for (int i = 2; i <= limit; i++) {
if (isPrime[i]) {
fprintf(fp, "%4d ", i);
if (++count % 10 == 0) fprintf(fp, "\n");
}
}
free(isPrime);
}
檔案模式對照表
| 模式 | 說明 | 檔案存在 | 檔案不存在 |
| r | 唯讀 | 開啟 | 失敗 |
| w | 寫入 (截斷) | 清空重寫 | 建立新檔 |
| a | 附加 | 移至末尾 | 建立新檔 |
| r+ | 讀寫 | 開啟 | 失敗 |
| w+ | 讀寫 (截斷) | 清空重寫 | 建立新檔 |
| a+ | 讀寫附加 | 移至末尾 | 建立新檔 |
常見錯誤與最佳實務
- 必檢查 fopen 回傳值:NULL 表示失敗
- 務必 fclose:否則資料可能留在緩衝區未寫入磁碟
- 路徑分隔符:Windows 用
\\ 或 /,Linux/macOS 用 /
- 編碼一致性:文字檔建議 UTF-8,跨平台相容
- 大量寫入效能:考慮
setvbuf 調整緩衝區或用二進位 fwrite