Kỹ năng làm bài thi lập trình C++: File I/O, freopen, Template & Mẹo thi
Bài học giúp học sinh chuyển từ “code chạy trên bàn phím” sang “code sẵn sàng nộp bài thi”: đọc/ghi file, dùng freopen an toàn, tạo template gọn, dùng define đúng cách và tránh các lỗi thường gặp.
Mục tiêu bài học
.INP và .OUT thay vì nhập tay.freopenBiết mẫu if(fopen(...)) freopen(...) để chạy được cả ở máy cá nhân và OJ.define, alias, hằng số và mẹo tránh lỗi khi làm bài thi.1. Vì sao phải đọc / ghi file?
Trong nhiều kỳ thi lập trình truyền thống, đề bài không yêu cầu nhập từ bàn phím trực tiếp. Thay vào đó:
BAI1.INP
Chứa dữ liệu đầu vào, ví dụ: hai số, mảng, ma trận, đồ thị.
BAI1.OUT
Chứa kết quả chương trình cần in ra để hệ thống chấm.
2. Cách đọc / ghi file cơ bản
Bài toán mẫu: File SUM.INP chứa hai số nguyên a, b. Ghi ra SUM.OUT tổng a + b.
#include
using namespace std;
int main() {
ifstream fin("SUM.INP"); // mở file để đọc
ofstream fout("SUM.OUT"); // mở file để ghi
long long a, b;
fin >> a >> b;
fout << a + b;
return 0;
} .INP và .OUT phải nằm cùng thư mục chạy chương trình. Nếu tên file sai một ký tự, chương trình có thể không đọc được dữ liệu.3. Cách dùng if(fopen(...)) freopen(...) trong C++
Trong OJ hiện đại, chương trình thường đọc từ stdin và in ra stdout. Nhưng khi luyện đề file trên máy, ta lại muốn đọc từ SUM.INP và ghi ra SUM.OUT. Mẫu dưới đây giải quyết cả hai trường hợp.
SUM.INP tồn tại thì chương trình chuyển cin sang đọc file và chuyển cout sang ghi file. Nếu file không tồn tại, chương trình vẫn dùng nhập/xuất chuẩn như bình thường.
#include
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Nếu file SUM.INP tồn tại thì đọc/ghi bằng file.
// Nếu không tồn tại, chương trình vẫn đọc từ bàn phím / stdin như OJ.
if (fopen("SUM.INP", "r")) {
freopen("SUM.INP", "r", stdin);
freopen("SUM.OUT", "w", stdout);
}
long long a, b;
cin >> a >> b;
cout << a + b;
return 0;
} SUM.INP → tự động đọc file, ghi SUM.OUT.SUM.INP → vẫn đọc bằng cin, in bằng cout.SUM, BAI1, PRODUCT,...4. Tương tác nhanh: chọn kiểu đọc/ghi phù hợp
Trình tạo mẫu freopen
Thử dữ liệu
Template tạo ra
5. Một số kỹ thuật define, alias và mẹo thi
Các kỹ thuật này giúp code gọn hơn, nhưng cần dùng có kiểm soát để học sinh không bị rối hoặc tạo lỗi khó tìm.
| Kỹ thuật | Nên dùng khi nào? | Lưu ý tránh nhầm |
|---|---|---|
using ll = long long; | Khi bài có số lớn, tổng lớn, tích lớn. | Rõ nghĩa hơn #define ll long long. |
#define all(x) | Khi dùng sort, reverse, lower_bound với vector. | Chỉ dùng với container có begin(), end(). |
const int MOD | Bài yêu cầu lấy dư. | Không tự lấy dư nếu đề không yêu cầu. |
ios::sync_with_stdio(false); cin.tie(nullptr); | Hầu như nên đặt đầu main để nhập/xuất nhanh. | Không trộn lung tung cin/cout với scanf/printf khi chưa hiểu rõ. |
#define int long long | Chỉ dành cho người đã có kinh nghiệm. | Học sinh mới học không nên dùng, dễ lỗi kiểu dữ liệu và hàm main. |
#include
using namespace std;
using ll = long long;
using pii = pair;
using vi = vector;
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()
const int INF = 1e9;
const ll LINF = (ll)4e18;
const int MOD = 1000000007;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector a = {3, 1, 5, 2};
sort(all(a));
for (int x : a) cout << x << ' ';
return 0;
} 6. Các lỗi học sinh hay mắc và cách tránh
- Sai tên file: Đề yêu cầu
BAI1.INPnhưng code lại mởbai1.inp. Nhiều hệ thống phân biệt chữ hoa/thường. - Quên mở file output: Đọc được input nhưng vẫn in ra màn hình, file
.OUTrỗng. - Dùng
ifstreamnhưng vẫn nhập bằngcin: Nếu đã tạofin, phải đọc bằngfin >> .... Nếu dùngfreopen, có thể đọc bằngcin. - Không kiểm tra dữ liệu: Đọc thiếu số, đọc sai định dạng, nhầm dòng đầu là
nhay là dữ liệu. - Lạm dụng macro: Macro quá nhiều làm code ngắn nhưng khó hiểu. Khi học sinh mới học, ưu tiên rõ ràng.
7. Quiz kiểm tra nhanh
Câu 1. Trong freopen("SUM.INP", "r", stdin), chữ r nghĩa là gì?
Câu 2. Muốn cout ghi ra file SUM.OUT, tham số cuối của freopen là gì?
Câu 3. Với học sinh mới học, lựa chọn nào an toàn hơn?
8. Bài tập tự luyện phân loại rõ ràng
Các bài dưới đây giúp học sinh luyện từ đọc/ghi file cơ bản đến viết template thi.
- SUM.INP: hai số
a, b. Ghi SUM.OUT:a+b. - PRODUCT.INP: hai số
a, b. Ghi PRODUCT.OUT:a*b.
- MAXNUM.INP: dòng 1 là
n, dòng 2 cónsố. Ghi số lớn nhất. - EVENCOUNT.INP: đếm số chẵn trong dãy.
- COUNTZERO.INP: ma trận
R x C. Ghi số phần tử bằng 0. - ROWSUM.INP: ghi tổng từng hàng của ma trận.
- Viết lại bài SUM bằng mẫu
if(fopen(...)) freopen(...). - Tạo template cá nhân có
solve(),using ll, fast I/O.
💻 Lời giải mẫu MAXNUM C++ / Python
#include
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
if (fopen("MAXNUM.INP", "r")) {
freopen("MAXNUM.INP", "r", stdin);
freopen("MAXNUM.OUT", "w", stdout);
}
int n;
cin >> n;
long long x, mx;
cin >> mx;
for (int i = 2; i <= n; i++) {
cin >> x;
if (x > mx) mx = x;
}
cout << mx;
return 0;
} 💻 Lời giải mẫu COUNTZERO C++ / Python
#include
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
if (fopen("COUNTZERO.INP", "r")) {
freopen("COUNTZERO.INP", "r", stdin);
freopen("COUNTZERO.OUT", "w", stdout);
}
int r, c;
cin >> r >> c;
int cnt = 0;
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
int x;
cin >> x;
if (x == 0) cnt++;
}
}
cout << cnt;
return 0;
} freopen giúp linh hoạt khi luyện trên máy và nộp OJ; template tốt là template ngắn, rõ, dễ debug.
➡ Tiếp theo: áp dụng template này vào các bài mảng, chuỗi, hàm và đồ thị.
💳 Quét mã ủng hộ tuỳ tâm nhé!