L11. Tập Tin (File I/O)

1. Giới Thiệu Về Tập Tin

1.1. Tại Sao Phải Sử Dụng Tập Tin?

Thông thường:

Nhập từ bàn phím → Lưu trên RAM → Xử lý → Xuất ra màn hình

Ưu điểm:

  • Tốc độ truy xuất cao
  • Đơn giản, dễ sử dụng

Nhược điểm:

  • RAM đắt tiền, dung lượng hạn chế
  • Mất điện → Mất dữ liệu
  • Không xử lý được Big Data
  • Không lưu trữ dài hạn

Giải pháp: Sử dụng Tập Tin

Nhập từ file → Xử lý trên RAM → Ghi vào file
       ↑                              ↓
       └──────────────────────────────┘
           Lưu trữ lâu dài trên ổ cứng

1.2. Khái Niệm Tập Tin

Tập tin (File):

  • Tập hợp thông tin được tổ chức theo dạng xác định
  • Có tên được định danh
  • Là dãy byte liên tục (góc độ lưu trữ)
  • Lưu trữ trên thiết bị ngoài: HDD, SSD, USB…
  • Cho phép đọc và ghi dữ liệu

1.3. Phân Loại Tập Tin

Theo mục đích sử dụng:

  • .EXE, .DOCX, .TXT, .PPT, .PDF

Theo cách sử dụng trong lập trình:

  1. Tập tin văn bản (Text file)

    • Chỉ chứa các ký tự
    • Tổ chức thành từng dòng
    • Kết thúc dòng: \0 hoặc \n
    • Có thể đọc được bằng notepad
  2. Tập tin nhị phân (Binary file)

    • Chứa các byte
    • Đọc/ghi chính xác từng byte
    • Không đọc được bằng text editor

2. Quy Tắc Đặt Tên Tập Tin

2.1. Cú Pháp

<Tên_tập_tin>.<Mở_rộng>

Tên tập tin:

  • Bắt buộc phải có
  • Tối đa 128 ký tự
  • Gồm: A-Z, a-z, 0-9, khoảng trắng, @#$%^()!
  • Không dùng: / \ : * ? " < > |

Mở rộng:

  • Không bắt buộc
  • Thường 3-4 ký tự
  • Ví dụ: .txt, .dat, .cpp, .exe

Ví dụ:

  • data.txt
  • my file 2024.dat
  • report#1.txt
  • file/name.txt ✗ (có dấu /)

2.2. Đường Dẫn (Path)

Đường dẫn:

Địa chỉ chỉ đến tập tin trên ổ cứng.

Ví dụ Windows:

C:\data\list.txt

Trong C++:

"C:\\data\\list.txt"  // Phải dùng \\

Đường dẫn tương đối:

"data.txt"           // Cùng thư mục với file .exe
"input\\data.txt"    // Trong thư mục con input
"..\\data.txt"       // Thư mục cha

3. Thao Tác Với Tập Tin

3.1. Quy Trình Cơ Bản

graph LR A[Mở tập tin] --> B[Đọc/Ghi dữ liệu] B --> C[Đóng tập tin]

So sánh với nhập/xuất chuẩn:

// Nhập/xuất chuẩn
#include <iostream>
cin >> variable;
cout << value;

// Nhập/xuất tập tin
#include <fstream>
ifstream >> variable;  // Đọc từ file
ofstream << value;     // Ghi vào file

3.2. Thư Viện fstream

#include <fstream>

Ba loại stream:

StreamMục đíchÝ nghĩa
ifstreamĐọc từ fileinput file stream
ofstreamGhi vào fileoutput file stream
fstreamĐọc và ghifile stream

4. Mở và Đóng Tập Tin

4.1. Mở Tập Tin Để Đọc

Cú pháp:

ifstream <tên_biến>(<đường_dẫn_tập_tin>);

Ví dụ:

ifstream input("test.txt");

if (!input) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

// Đọc dữ liệu

input.close();

4.2. Mở Tập Tin Để Ghi

Cú pháp:

ofstream <tên_biến>(<đường_dẫn_tập_tin>);

Ví dụ:

ofstream output("test.txt");

if (!output) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

// Ghi dữ liệu

output.close();

4.3. Ghi Tích Hợp Vào Cuối File

Sử dụng ios::app (append):

ofstream output("test.txt", ios::app);

if (!output) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

// Ghi thêm vào cuối file

output.close();

4.4. Kiểm Tra và Đóng File

Kiểm tra mở file thành công:

ifstream in("test.txt");

if (!in) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

// Hoặc
if (in.fail()) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

Đóng file:

in.close();   // Đóng file input
out.close();  // Đóng file output

5. Ghi Dữ Liệu Vào Tập Tin

5.1. Ghi Cơ Bản

Cú pháp:

ofstream out(<tên_file>);
out << <dữ_liệu>;

Ví dụ 1: Ghi số và chuỗi

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ofstream out("test.txt");
    
    if (!out) {
        cout << "Khong mo duoc file!" << endl;
        return 1;
    }
    
    out << 10 << "\t" << 123.23 << endl;
    out << "HelloCplusplus.";
    
    out.close();
    return 0;
}

Nội dung file test.txt:

10      123.23
HelloCplusplus.

5.2. Ghi Tích Hợp (Append)

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    // Ghi lần 1
    ofstream out("test.txt");
    out << "Line 1" << endl;
    out.close();
    
    // Ghi tích hợp
    ofstream out2("test.txt", ios::app);
    out2 << "Line 2" << endl;
    out2.close();
    
    return 0;
}

Nội dung file test.txt:

Line 1
Line 2

5.3. Ghi Mảng Vào File

void ghiMang(int a[], int n, const char* filename) {
    ofstream out(filename);
    
    if (!out) {
        cout << "Khong mo duoc file!" << endl;
        return;
    }
    
    out << n << endl;  // Ghi số phần tử
    for (int i = 0; i < n; i++) {
        out << a[i] << " ";
    }
    
    out.close();
}

6. Đọc Dữ Liệu Từ Tập Tin

6.1. Đọc Cơ Bản

Cú pháp:

ifstream in(<tên_file>);
in >> <biến>;

Ví dụ: Đọc số và chuỗi

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    char ch;
    int i;
    float f;
    char str[80];
    
    ifstream in("test.txt");
    
    if (!in) {
        cout << "Khong mo duoc file!" << endl;
        return 1;
    }
    
    in >> i;      // Đọc số nguyên
    in >> f;      // Đọc số thực
    in >> ch;     // Đọc ký tự
    in >> str;    // Đọc chuỗi
    
    cout << i << " " << f << " " << ch << endl;
    cout << str << endl;
    
    in.close();
    return 0;
}

6.2. Đọc Từng Ký Tự

Sử dụng get():

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    char ch;
    ifstream in("test.txt");
    
    if (!in) {
        cout << "Khong mo duoc file!" << endl;
        return 1;
    }
    
    while (!in.eof()) {
        in.get(ch);
        if (!in.eof())  // Tránh in ký tự cuối lặp lại
            cout << ch;
    }
    
    in.close();
    return 0;
}

6.3. Đọc Từng Dòng

Sử dụng getline():

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    ifstream in("test.txt");
    
    if (!in) {
        cout << "Khong mo duoc file!" << endl;
        return 1;
    }
    
    // Đọc từng dòng
    for (string str; getline(in, str);) {
        cout << str << endl;
    }
    
    in.close();
    return 0;
}

6.4. Hàm Kiểm Tra Cuối File

Hàm eof() (End Of File):

while (!in.eof()) {
    // Đọc dữ liệu
}

Ví dụ đọc mảng:

void docMang(int a[], int &n, const char* filename) {
    ifstream in(filename);
    
    if (!in) {
        cout << "Khong mo duoc file!" << endl;
        return;
    }
    
    in >> n;  // Đọc số phần tử
    for (int i = 0; i < n; i++) {
        in >> a[i];
    }
    
    in.close();
}

7. Bài Tập Minh Họa

Bài Tập 1: Tính Trung Bình

Yêu cầu:

Tạo file trung binh.txt có nội dung:

46  56  12
12  34  56
45  78  90

Tính trung bình mỗi cột, ghi tích hợp vào file:

46  56  12
12  34  56
45  78  90
34.33  56  52.66

Giải:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;

int main() {
    // Tạo file ban đầu
    ofstream out("trung binh.txt");
    out << 46 << "\t" << 56 << "\t" << 12 << endl;
    out << 12 << "\t" << 34 << "\t" << 56 << endl;
    out << 45 << "\t" << 78 << "\t" << 90 << endl;
    out.close();
    
    // Đọc và tính trung bình
    ifstream in("trung binh.txt");
    float s1 = 0, s2 = 0, s3 = 0;
    int step = 0, t;
    
    for (string str; getline(in, str);) {
        istringstream iss(str);
        step = 0;
        
        do {
            string sub;
            iss >> sub;
            if (sub != "") {
                t = atoi(sub.c_str());
                if (step == 0) s1 += t;
                else if (step == 1) s2 += t;
                else if (step == 2) s3 += t;
                ++step;
            }
        } while (iss);
    }
    
    s1 /= 3; s2 /= 3; s3 /= 3;
    in.close();
    
    // Làm tròn 2 chữ số
    s1 = (int)(s1 * 100) / 100.0;
    s2 = (int)(s2 * 100) / 100.0;
    s3 = (int)(s3 * 100) / 100.0;
    
    // Ghi tích hợp
    ofstream out2("trung binh.txt", ios::app);
    out2 << endl;
    out2 << s1 << "\t" << s2 << "\t" << s3 << endl;
    out2.close();
    
    return 0;
}

Bài Tập 2: Quản Lý Mảng Số Nguyên

Yêu cầu:

Nhập mảng số nguyên, ghi vào file Integer.txt:

  • Dòng 1: “Begin Day”
  • Dòng 2: Số phần tử
  • Các dòng tiếp: Mỗi dòng 20 phần tử
  • Dòng cuối: “End Day”

Đọc file và tìm phần tử lớn nhất.

Giải:

#include <iostream>
#include <fstream>
using namespace std;

void ghiMang(int a[], int n) {
    ofstream out("Integer.txt");
    
    out << "Begin Day" << endl;
    out << n << endl;
    
    for (int i = 0; i < n; i++) {
        out << a[i] << " ";
        if ((i + 1) % 20 == 0)
            out << endl;
    }
    
    out << endl << "End Day" << endl;
    out.close();
}

int timMax() {
    ifstream in("Integer.txt");
    
    string temp;
    getline(in, temp);  // Bỏ "Begin Day"
    
    int n;
    in >> n;
    
    int max, x;
    in >> max;  // Phần tử đầu tiên
    
    for (int i = 1; i < n; i++) {
        in >> x;
        if (x > max)
            max = x;
    }
    
    in.close();
    return max;
}

int main() {
    int a[100], n;
    
    cout << "Nhap n: ";
    cin >> n;
    
    cout << "Nhap mang:\n";
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    
    ghiMang(a, n);
    
    int max = timMax();
    cout << "Phan tu lon nhat: " << max << endl;
    
    return 0;
}

Bài Tập 3: Ma Trận

Yêu cầu:

Nhập ma trận vuông, ghi vào matran.txt:

  • Dòng 1: Cấp ma trận
  • Các dòng tiếp: Mỗi dòng của ma trận

Đọc file, tìm min, max, dòng có tổng lớn nhất.

Giải:

#include <iostream>
#include <fstream>
using namespace std;

void ghiMaTran(int a[][100], int n) {
    ofstream out("matran.txt");
    
    out << n << endl;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            out << a[i][j] << " ";
        }
        out << endl;
    }
    
    out.close();
}

void timMinMax(int &min, int &max) {
    ifstream in("matran.txt");
    
    int n;
    in >> n;
    
    int x;
    in >> x;
    min = max = x;
    
    for (int i = 0; i < n * n - 1; i++) {
        in >> x;
        if (x < min) min = x;
        if (x > max) max = x;
    }
    
    in.close();
}

int main() {
    int a[100][100], n;
    
    cout << "Nhap cap ma tran: ";
    cin >> n;
    
    cout << "Nhap ma tran:\n";
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> a[i][j];
        }
    }
    
    ghiMaTran(a, n);
    
    int min, max;
    timMinMax(min, max);
    
    cout << "Min: " << min << endl;
    cout << "Max: " << max << endl;
    
    return 0;
}

8. Bài Tập Bắt Buộc


9. Tổng Kết và Lưu Ý

9.1. Quy Trình Làm Việc Với File

graph TD A[Khai báo stream] --> B[Mở file] B --> C{Mở thành công?} C -->|Không| D[Báo lỗi, thoát] C -->|Có| E[Đọc/Ghi dữ liệu] E --> F[Đóng file]

9.2. Bảng Tổng Hợp

Thao tácifstream (Đọc)ofstream (Ghi)
Mở fileifstream in("file.txt");ofstream out("file.txt");
Ghi thêm cuối-ofstream out("file.txt", ios::app);
Đọc/Ghiin >> variable;out << value;
Đọc dònggetline(in, str);-
Đọc ký tựin.get(ch);-
Kiểm tra cuối filein.eof()-
Đóng filein.close();out.close();

9.3. Các Lưu Ý Quan Trọng

9.4. So Sánh iostream vs fstream

iostreamfstreamChức năng
cinifstreamĐọc dữ liệu
coutofstreamGhi dữ liệu
>>>>Toán tử nhập
<<<<Toán tử xuất
getline(cin, str)getline(in, str)Đọc dòng