14.4 freopen:一行把鍵盤螢幕換成檔案
ifstream 好用,但拿來做本機測試有個小麻煩:程式裡所有的 cin 都得改成 fin,測完要上傳,又得全部改回來。改來改去,總有一次會改漏。
freopen 走另一條路:不動 cin,動它背後的線路。它把「標準輸入」這條線從鍵盤拔下來、插到檔案上——cin 本人毫不知情,照常從標準輸入讀,只是讀到的已經是檔案內容:
freopen("input.txt", "r", stdin); // "r" = read:cin 從此改讀 input.txt
freopen("output.txt", "w", stdout); // "w" = write:cout 從此改寫 output.txt
三個參數依序是:檔名、模式("r" 讀、"w" 寫)、要接管的線路(stdin 標準輸入、stdout 標準輸出)。它是 C 語言 <cstdio> 家族的函式,記得引入標頭檔。
¶範例程式碼
把 14.2 的加法程式改用 freopen——注意讀寫的部分一行都不用改,就是原汁原味的 cin/cout:
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
freopen("input.txt", "r", stdin); // 換線路的兩行,加在最前面
freopen("output.txt", "w", stdout);
int a, b;
cin >> a >> b; // 以下跟平常寫題完全一樣
cout << a + b << '\n';
return 0;
}
執行時螢幕一片安靜。執行結束後,output.txt 的內容:
42
這就是競程本機測試的標準做法:寫題照常寫 cin/cout,要測試時在 main 開頭加這兩行,測完上傳前把這兩行註解掉。整支程式跟「交卷版」的差異永遠只有兩行。
¶一勞永逸:#ifdef LOCAL 自動切換模板
12.9 學過 #ifdef:被 #ifdef 名字 和 #endif 夾住的程式碼,只有那個名字被 define 過才存在。搭配編譯參數 -D,就能讓 freopen「只在自己電腦上存在」:
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin); // 只有編譯時加了 -DLOCAL,這兩行才存在
freopen("output.txt", "w", stdout);
#endif
// ↓ 從這裡開始寫題目,跟平常完全一樣
int x;
long long sum = 0;
while (cin >> x) sum += x;
cout << sum << '\n';
return 0;
}
本機編譯時加上 -DLOCAL,freopen 生效、程式讀寫檔案;不加,那兩行就像不存在,程式照常讀鍵盤寫螢幕:
g++ -std=c++17 -O2 -DLOCAL -o sol sol.cpp ← 本機測試版:讀 input.txt
g++ -std=c++17 -O2 -o sol sol.cpp ← 乾淨版:讀標準輸入
兩種編譯方式:加 -DLOCAL 執行後 output.txt 得到 42(讀的是 14.2 那份 input.txt);不加 -DLOCAL、從標準輸入餵 1 2 3,螢幕印出 6。同一份程式碼,零修改。
動手試試看:把範例的 freopen 兩行註解掉(或拿掉 -DLOCAL)再跑一次,確認程式回到「等鍵盤輸入」的狀態——體會「一行都不用改」反方向也成立。