Bài 18.122 phút đọc
Luồng và kiểu FILE
Sau bài này bạn sẽ làm được
- Giải thích FILE là kiểu mờ và vì sao không được tự tạo đối tượng FILE
- Phân biệt ba chế độ đệm và biết chế độ mặc định của từng luồng
- Chỉ ra vì sao printf không hiện gì khi chương trình sập
- Dùng setvbuf và fflush đúng lúc
Mọi chương trình từ Phần 0 tới giờ đều đã đọc ghi file mà bạn không để ý: printf ghi vào một file, scanf đọc từ một file. Bài này mở nắp xem bên dưới có gì, và giải thích một hiện tượng đã làm phiền bạn nhiều lần mà chưa có lời giải.
#Luồng là gì
Luồng
Một dãy byte có thứ tự, đọc hoặc ghi tuần tự từ đầu tới cuối. Thư viện chuẩn C không phân biệt byte đó tới từ đâu: file trên đĩa, bàn phím, màn hình, máy in hay một chương trình khác đều là luồng.
Chuong trinh cua ban
|
| fputs, fprintf, fputc <- cung mot bo ham
v
[ LUONG ]
|
+--> tep tren dia
+--> man hinh
+--> may in
+--> mot chuong trinh khac (ong dan)
+--> ket noi mang (tren Linux)#Kiểu FILE là kiểu mờ
#include <stdio.h>
FILE *f; /* CON TRO toi FILE, khong bao gio la FILE */Kiểu mờ
Kiểu mà bạn biết tên nhưng không biết bên trong có gì. Thư viện chỉ cho bạn con trỏ tới nó và một bộ hàm để thao tác. Bạn không được đọc trường, không được sao chép, không được tự tạo.
Đụng vào ruột FILE
FILE f; /* khong the: kich thuoc va noi dung
la chuyen rieng cua thu vien */
FILE *g = fopen("a.txt", "r");
FILE h = *g; /* SAO CHEP: hong.
Hai doi tuong cung tro toi mot bo dem,
dong mot cai la cai kia thanh rac. */
printf("%d\n", g->_flags); /* KHONG KHA CHUYEN: ten truong
khac nhau giua cac thu vien */Chỉ dùng con trỏ và các hàm
FILE *f = fopen("a.txt", "r"); /* chi giu con tro */
if (feof(f)) { /* ... */ } /* hoi trang thai bang HAM */
if (ferror(f)) { /* ... */ }
long vt = ftell(f); /* hoi vi tri bang HAM */
fclose(f); /* tra lai cho thu vien */Bên trong nó có gì
Bạn không cần biết, nhưng biết thì hiểu được vì sao các hàm hành xử như vậy. Mọi cài đặt đều giữ ít nhất bốn thứ sau.
| Thứ | Dùng để làm gì | Hàm nào chạm tới |
|---|---|---|
| Bộ đệm | Gom nhiều lần ghi nhỏ thành một lần ghi lớn | fflush, setvbuf |
| Vị trí hiện tại | Lần đọc ghi tiếp theo bắt đầu từ đâu | fseek, ftell, rewind |
| Cờ kết thúc và cờ lỗi | Nhớ rằng lần đọc trước đã thất bại | feof, ferror, clearerr |
| Số hiệu file của hệ điều hành | Cầu nối xuống nhân hệ điều hành | fileno trên POSIX |
#Ba luồng mở sẵn
| Luồng | Số hiệu | Mặc định nối với | Chế độ đệm |
|---|---|---|---|
| stdin | 0 | Bàn phím | Theo dòng |
| stdout | 1 | Màn hình | Theo dòng khi ra màn hình, đầy mới xả khi chuyển hướng |
| stderr | 2 | Màn hình | Không đệm, luôn luôn |
#Bộ đệm và ba chế độ
setvbuf
int setvbuf(FILE *f, char *bo_dem, int che_do, size_t co);
/* che_do: _IOFBF (day moi xa) · _IOLBF (moi dong) · _IONBF (khong dem) */
FILE *f = fopen("nhat-ky.txt", "w");
if (!f) return 1;
setvbuf(f, NULL, _IOLBF, 0); /* de thu vien tu cap bo dem */
/* Bay gio moi dong nhat ky ra dia ngay khi ghi xong.
Cham hon, nhung neu chuong trinh sap thi khong mat dong nao. */#Khi nào dữ liệu biến mất
mat.c
#include <stdio.h>
int main(void) {
printf("A: nam trong bo dem\n");
fflush(stdout); /* xa tay */
printf("B: cung nam trong bo dem\n");
volatile int *p = 0;
*p = 1; /* sap that su */
return 0;
}terminal
gcc -std=c11 -o mat mat.c && ./mat > ra.txt
Segmentation fault
cat ra.txt
A: nam trong bo dem
Tự làm thử
- Chạy chương trình đo tốc độ ở trên trên máy của bạn và ghi lại hai con số.
- Viết chương trình in mười dòng rồi cố ý sập, chạy nó hai lần: một lần ra màn hình, một lần chuyển hướng vào file. So sánh số dòng thu được.
- Thêm
setvbuf(stdout, NULL, _IONBF, 0)vào chương trình trên và chạy lại, xác nhận không mất dòng nào. - Viết một chương trình in ra
stdoutvà một chương trình in rastderr, rồi thử./ct > /dev/nullvới cả hai. - Cấp một bộ đệm tám byte bằng
setvbufrồi ghi mười hai byte, xác nhận file vẫn đủ mười hai byte.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Luồng là dãy byte, và cùng một bộ hàm dùng được cho file, màn hình, bàn phím và ống dẫn.
FILElà kiểu mờ. Chỉ giữ con trỏ, không sao chép, không đọc trường bên trong.stderrkhông đệm, nên thông báo lỗi phải đi qua nó, cả vì lý do đúng đắn lẫn vì nó không bị mất.- Bộ đệm làm chương trình nhanh hơn hàng trăm lần, và làm dữ liệu biến mất khi chương trình sập.
fflushđẩy dữ liệu tới hệ điều hành, còn muốn chắc chắn nó nằm trên đĩa thì cần thêmfsync.