
Bài 21 Qu n lý t p tinả ậ
M c tiêu:ụ
K t thúc bài h c này, b n có th :ế ọ ạ ể
Gi i thích khái ni m lu ng (streams) và t p tin (files)ả ệ ồ ậ
Th o lu n các lu ng văn b n và các lu ng nh phânả ậ ồ ả ồ ị
Gi i thích các hàm x lý t p tin ả ử ậ
Gi i thích con tr t p tinả ỏ ậ
Th o lu n con tr kích ho t hi n hànhả ậ ỏ ạ ệ
Gi i thích các đ i s t dòng nh c l nh (command-line).ả ố ố ừ ắ ệ
Gi i thi uớ ệ
H u h t các ch ng trình đ u yêu c u đ c và ghi d li u vào các h th ng l u tr trên đĩa. Cácầ ế ươ ề ầ ọ ữ ệ ệ ố ư ữ
ch ng trình x lý văn b n c n l u các t p tin văn b n, ch ng trình x lý b ng tính c n l u n iươ ử ả ầ ư ậ ả ươ ử ả ầ ư ộ
dung c a các ô, ch ng trình c s d li u c n l u các m u tin. Bài này s khám phá các ti n íchủ ươ ơ ỡ ữ ệ ầ ư ẫ ẽ ệ
trong C dành cho các thao tác nh p/xu t (I/O) đĩa h th ng.ậ ấ ệ ố
Ngôn ng C không ch a b t kỳ câu l nh nh p/xu t nào m t cách t ng minh. T t c các thao tácữ ứ ấ ệ ậ ấ ộ ườ ấ ả
nh p/xu t đ u th c hi n thông qua các hàm th vi n chu n c a C. Ti p c n này làm cho h th ngậ ấ ề ự ệ ư ệ ẩ ủ ế ậ ệ ố
qu n lý t p tin c a C r t m nh và uy n chuy n. Nh p/xu t trong C là tuy t v i vì d li u có thả ậ ủ ấ ạ ể ể ậ ấ ệ ờ ữ ệ ể
truy n d ng nh phân hay d ng văn b n mà con ng i có th đ c đ c. Đi u này làm cho vi cề ở ạ ị ở ạ ả ườ ể ọ ượ ề ệ
t o t p tin đ đáp ng m i nhu c u m t cách d dàng.ạ ậ ể ứ ọ ầ ộ ễ
Vi c hi u rõ s khác bi t gi a stream và t p tin là r t quan tr ng. H th ng nh p/xu t c a C cungệ ể ự ệ ữ ậ ấ ọ ệ ố ậ ấ ủ
c p cho ng i dùng m t giao di n đ c l p v i thi t b th t s đang truy c p. Giao di n này khôngấ ườ ộ ệ ộ ậ ớ ế ị ậ ự ậ ệ
ph i là m t t p tin th t s mà là m t s bi u di n tr u t ng c a thi t b . Giao di n tr u t ngả ộ ậ ậ ự ộ ự ễ ễ ừ ượ ủ ế ị ệ ừ ượ
này đ c g i là m t stream và thi t b th t s đ c g i là ượ ọ ộ ế ị ậ ự ượ ọ t p tinậ.
21.1 File Streams
H th ng t p tin c a C làm vi c đ c v i r t nhi u thi t b khác nhau bao g m máy in, đĩa, ệ ố ậ ủ ệ ượ ớ ấ ề ế ị ồ ổ ổ
băng t và các thi t b đ u cu i. M c dù t t c các thi t b đ u khác nhau, nh ng h th ng t p tinừ ế ị ầ ố ặ ấ ả ế ị ề ư ệ ố ậ
có vùng đ m s chuy n m i thi t b v m t thi t b logic g i là m t stream. Vì m i ệ ẽ ể ỗ ế ị ề ộ ế ị ọ ộ ọ streams ho tạ
đ ng t ng t , nên vi c qu n lý các thi t b là r t d dàng. Có hai lo i streams – ộ ươ ự ệ ả ế ị ấ ễ ạ văn b nả (text) và
nh phânị (binary).
21.1.1 Streams văn b nả
M t ộstreams văn b nả là m t chu i các ký t . Các streams văn b n có th đ c t ch c thành cácộ ỗ ự ả ể ượ ổ ứ
dòng, m i dòng k t thúc b ng m t ký t sang dòng m i. Tuy nhiên, ký t sang dòng m i là tùyỗ ế ằ ộ ự ớ ự ớ
ch n trong dòng cu i và đ c quy t đ nh khi cài đ t. H u h t các trình biên d ch C không k t thúcọ ố ượ ế ị ặ ầ ế ị ế
stream văn b n v i ký t sang dòng m i. Trong m t stream văn b n, có th x y ra m t vài sả ớ ự ớ ộ ả ể ả ộ ự
chuy n đ i ký t khi môi tr ng yêu c u. Ch ng h n nh , ký t sang dòng m i có th đ cể ổ ự ườ ầ ẳ ạ ư ự ớ ể ượ
chuy n thành m t c p ký t v đ u dòng/nh y đ n dòng k . Vì v y, m i quan h gi a các ký tể ộ ặ ự ề ầ ả ế ế ậ ố ệ ữ ự
đ c ghi (hay đ c) và nh ng ký t thi t b ngo i vi có th không ph i là m i quan h m t-m t.ượ ọ ữ ự ở ế ị ạ ể ả ố ệ ộ ộ
Và cũng vì s chuy n đ i có th x y ra này, s l ng ký t đ c ghi (hay đ c) có th không gi ngự ể ổ ể ả ố ượ ự ượ ọ ể ố
nh s l ng ký t nhìn th y thi t b ngo i vi.ư ố ượ ự ấ ở ế ị ạ
Qu n lý t p tinả ậ 29

21.1.2 Streams nh phânị
M t ộstreams nh phânị là m t chu i các byte v i s t ng ng m t-m t v i thi t b ngo i vi,ộ ỗ ớ ự ươ ứ ộ ộ ớ ế ị ạ
nghĩa là, không có s chuy n đ i ký t . Cũng vì v y, s l ng byte đ c (hay ghi) cũng s gi ngự ể ổ ự ậ ố ượ ọ ẽ ố
nh s l ng byte thi t b ngo i vi. Các stream nh phân là các chu i byte thu n túy, mà không cóư ố ượ ở ế ị ạ ị ỗ ầ
b t kỳ ký hi u nào đ c dùng đ ch ra đi m k t thúc c a t p tin hay k t thúc c a record. K t thúcấ ệ ượ ể ỉ ể ế ủ ậ ế ủ ế
c a t p tin đ c xác đ nh b ng đ l n c a t p tin.ủ ậ ượ ị ằ ộ ớ ủ ậ
21.2 Các hàm v t p tin và structure FILEề ậ
M t t p tin có th tham chi u đ n b t c cái gì: t m t t p tin trên đĩa đ n m t thi t b đ u cu iộ ậ ể ế ế ấ ứ ừ ộ ậ ế ộ ế ị ầ ố
hay m t máy in. Tuy nhiên, t t c các t p tin đ u không có cùng kh năng. Ví d nh , m t t p tinộ ấ ả ậ ề ả ụ ư ộ ậ
trên đĩa có th h tr truy c p ng u nhiên trong khi m t bàn phím thì không. M t t p tin s k t h pể ổ ợ ậ ẩ ộ ộ ậ ẽ ế ợ
v i m t stream b ng cách th c hi n thao tác m . T ng t , nó s thôi k t h p v i m t streamớ ộ ằ ự ệ ở ươ ự ẽ ế ợ ớ ộ
b ng thao tác đóng. Khi m t ch ng trình k t thúc bình th ng, t t c các t p tin đ u t đ ngằ ộ ươ ế ườ ấ ả ậ ề ự ộ
đóng. Tuy nhiên, khi m t ch ng trình b treo ho c k t thúc b t th ng, các t p tin v n còn m .ộ ươ ị ặ ế ấ ườ ậ ẫ ở
21.2.1 Các hàm c b n v t p tinơ ả ề ậ
M t h th ng qu n lý t p tin theo chu n ANSI bao g m m t s hàm liên quan v i nhau. Các hàmộ ệ ố ả ậ ẩ ồ ộ ố ớ
thông d ng nh t đ c li t kê trong b ng 21.1.ụ ấ ượ ệ ả
Name Function
fopen() M m t t p tinở ộ ậ
fclose() Đóng m t t p tinộ ậ
fputc() Ghi m t ký t vào m t t p tinộ ự ộ ậ
fgetc() Đ c m t ký t t m t t p tinọ ộ ự ừ ộ ậ
fread() Đ c t m t t p tin vào m t vùng đ mọ ừ ộ ậ ộ ệ
fwrite() Ghi t m t vùng đ m vào t p tinừ ộ ệ ậ
fseek() Tìm m t v trí nào đó trong t p tinộ ị ậ
fprintf() Ho t đ ng gi ng nh printf(), nh ng trên m t t p tinạ ộ ố ư ư ộ ậ
fscanf() Ho t đ ng gi ng nh scanf(), nh ng trên m t t p tinạ ộ ố ư ư ộ ậ
feof() Tr v true n u đã đ n cu i t p tin (end-of-file)ả ề ế ế ố ậ
ferror() Tr v true n u x y ra m t l iả ề ế ả ộ ỗ
rewind() Đ t l i con tr đ nh v trí (position locator) bên trong t p tin v đ u t pặ ạ ỏ ị ị ậ ề ầ ậ
tin
remove() Xóa m t t p tinộ ậ
fflush() Ghi d li u t m t vùng đ m bên trong vào m t t p tin xác đ nhữ ệ ừ ộ ệ ộ ậ ị
B ng 21.1: Các hàm c b n v t p tinả ơ ả ề ậ
Các hàm trên ch a trong t p tin header ứ ậ stdio.h. T p tin header này ph i đ c bao g m vào ch ngậ ả ượ ồ ươ
trình có s d ng các hàm này. H u h t các hàm này t ng t nh các hàm nh p/xu t t thi t bử ụ ầ ế ươ ự ư ậ ấ ừ ế ị
nh p xu t chu n. T p tin header ậ ấ ẩ ậ stdio.h còn đ nh nghĩa m t s macro s d ng trong quá trình x lýị ộ ố ử ụ ử
t p tin. Ví d nh , macro EOF đ c đ nh nghĩa là -1, ch a giá tr tr v khi m t hàm c đ c ti pậ ụ ư ượ ị ứ ị ả ề ộ ố ọ ế
khi đã đ n cu i t p tin.ế ố ậ
21.2.2 Con tr t p tinỏ ậ
M t con tr t p tin (file pointer) r t c n thi t cho vi c đ c và ghi các t p tin. Nó là m t con tr đ nộ ỏ ậ ấ ầ ế ệ ọ ậ ộ ỏ ế
m t structure ch a thông tin v t p tin. Thông tin bao g m: tên t p tin, v trí hi n t i c a t p tin,ộ ứ ề ậ ồ ậ ị ệ ạ ủ ậ
t p tin đang đ c đ c hay ghi, có b t kỳ l i nào xu t hi n hay đã đ n cu i t p tin. Ng i dùngậ ượ ọ ấ ỗ ấ ệ ế ố ậ ườ
30 L p trình c b n Cậ ơ ả

không c n thi t ph i bi t chi ti t, vì các đ nh nghĩa l y t studio.h có bao g m m t khai báoầ ế ả ế ế ị ấ ừ ồ ộ
structure tên là FILE. Câu l nh khai báo duy nh t c n thi t cho m t con tr t p tin là:ệ ấ ầ ế ộ ỏ ậ
FILE *fp;
Khai báo này cho bi t ếfp là m t con tr tr đ n m t FILE.ộ ỏ ỏ ế ộ
21.3 Các t p tin văn b nậ ả
Có nhi u hàm khác nhau đ qu n lý t p tin văn b n. Chúng ta s th o lu n trong các đo n bênề ể ả ậ ả ẽ ả ậ ạ
d i:ướ
21.3.1 M m t t p tin văn b nở ộ ậ ả
Hàm fopen() m m t stream đ s d ng và liên k t m t t p tin v i stream đó. Con tr k t h p v iở ộ ể ử ụ ế ộ ậ ớ ỏ ế ợ ớ
t p tin đ c tr v t hàm ậ ượ ả ề ừ fopen(). Trong h u h t các tr ng h p, t p tin đang m là m t t p tinầ ế ườ ợ ậ ở ộ ậ
trên đĩa. Nguyên m u c a hàm ẫ ủ fopen() là:
FILE *fopen(const char *filename, const char *mode);
trong đó filename là m t con tr tr đ n chu i ký t ch a m t tên t p tin h p l và cũng có thộ ỏ ỏ ế ỗ ự ứ ộ ậ ợ ệ ể
ch a c ph n mô t đ ng d n. Chu i đ c tr đ n b i con tr ứ ả ầ ả ườ ẫ ỗ ượ ỏ ế ở ỏ mode xác đ nh cách th c t p tinị ứ ậ
đ c m . B ng 21.2 li t kê các ch đ h p l mà m t t p tin có th m . ượ ở ả ệ ế ộ ợ ệ ộ ậ ể ở
Chế
độÝ nghĩa
r M m t t p tin văn b n đ đ cở ộ ậ ả ể ọ
w T o m t t p tin văn b n đ ghiạ ộ ậ ả ể
a N i vào m t t p tin văn b nố ộ ậ ả
r+ M m t t p tin văn b n đ đ c/ghiở ộ ậ ả ể ọ
w+ T o m t t p tin văn b n đ đ c/ghiạ ộ ậ ả ể ọ
a+f N i ho c t o m t t p tin văn b n đ đ c/ố ặ ạ ộ ậ ả ể ọ
ghi
B ng 21.2: Các ch đ m t p tin văn b n.ả ế ộ ở ậ ả
B ng 21.2 cho th y các t p tin có th đ c m nhi u ch đ khác nhau. M t con tr null đ cả ấ ậ ể ượ ở ở ề ế ộ ộ ỏ ượ
tr v n u x y ra l i khi hàm fopen() m t p tin. L u ý r ng các chu i nh “a+f” có th đ c bi uả ề ế ả ỗ ở ậ ư ằ ỗ ư ể ượ ễ
di n nh “af+”.ễ ư
N u ph i m m t t p tin ế ả ở ộ ậ xyz đ ghi, câu l nh s là:ể ệ ẽ
FILE *fp;
fp = fopen ("xyz", "w");
Tuy nhiên, m t t p tin nói chung đ c m b ng cách s d ng m t t p h p các câu l nh t ng tộ ậ ượ ở ằ ử ụ ộ ậ ợ ệ ươ ự
nh sau:ư
FILE *fp;
if ((fp = fopen ("xyz", "w")) == NULL)
{
printf("Cannot open file");
exit (1);
}
Qu n lý t p tinả ậ 31

Macro NULL đ c đ nh nghĩa trong stdio.h là ‘ượ ị \0’. N u s d ng ph ng pháp trên đ m m t t pế ử ụ ươ ể ở ộ ậ
tin, thì hàm fopen() s phát hi n ra l i n u có, ch ng h n nh đĩa đang ch đ c m ghi (write-ẽ ệ ỗ ế ẳ ạ ư ở ế ộ ấ
protected) hay đĩa đ y, tr c khi b t đ u ghi đĩa.ầ ướ ắ ầ
N u m t t p tin đ c m đ ghi, b t kỳ m t t p tin nào có cùng tên và đang m s b vi t ch ngế ộ ậ ượ ở ể ấ ộ ậ ở ẽ ị ế ồ
lên. Vì khi m t t p tin đ c m ch đ ghi, thì m t t p tin m i đ c t o ra. N u mu n n i thêmộ ậ ượ ở ở ế ộ ộ ậ ớ ượ ạ ế ố ố
các m u tin vào t p tin đã có, thì nó ph i đ c m v i ch đ “a”. N u m t t p tin đ c m chẫ ậ ả ượ ở ớ ế ộ ế ộ ậ ượ ở ở ế
đ đ c và nó không t n t i, hàm s tr v l i. N u m t t p tin đ c m đ đ c/ghi, nó s khôngộ ọ ồ ạ ẽ ả ề ỗ ế ộ ậ ượ ở ể ọ ẽ
b xóa n u đã t n t i. Tuy nhiên, n u nó không t n t i, thì nó s đ c t o ra.ị ế ồ ạ ế ồ ạ ẽ ượ ạ
Theo chu n ANSI, tám t p tin có th đ c m t i m t th i đi m. Tuy v y, h u h t các trình biênẩ ậ ể ượ ở ạ ộ ờ ể ậ ầ ế
d ch C và môi tr ng đ u cho phép m nhi u h n tám t p tin.ị ườ ề ở ề ơ ậ
21.3.2 Đóng m t t p tin văn b nộ ậ ả
Vì s l ng t p tin có th m t i m t th i đi m b gi i h n, vi c đóng m t t p tin khi không cònố ượ ậ ể ở ạ ộ ờ ể ị ớ ạ ệ ộ ậ
s d ng là m t đi u quan tr ng. Thao tác này s gi i phóng tài nguyên và làm gi m nguy c v tử ụ ộ ề ọ ẽ ả ả ơ ượ
quá gi i h n đã đ nh. Đóng m t stream cũng s làm s ch và chép vùng đ m k t h p c a nó ra ngoàiớ ạ ị ộ ẽ ạ ệ ế ợ ủ
(m t thao tác quan tr ng đ tránh m t d li u) khi ghi ra đĩa. Hàm ộ ọ ể ấ ữ ệ fclose() đóng m t stream đãộ
đ c m b ng hàm ượ ở ằ fopen(). Nó ghi b t kỳ d li u nào còn l i trong vùng đ m c a đĩa vào t p tin.ấ ữ ệ ạ ệ ủ ậ
Nguyên m u c a hàm ẫ ủ fclose() là:
int fclose(FILE *fp);
trong đó fp là m t con tr t p tin. Hàm ộ ỏ ậ fclose() tr v ả ề 0 n u đóng thành công. B t kỳ giá tr tr vế ấ ị ả ề
nào khác 0 đ u cho th y có l i x y ra. Hàm ề ấ ỗ ả fclose() s th t b i n u đĩa đã s m đ c g ra kh i ẽ ấ ạ ế ớ ượ ỡ ỏ ổ
đĩa ho c đĩa b đ y.ặ ị ầ
M t hàm khác dùng đ đóng stream là hàm ộ ể fcloseall(). Hàm này h u d ng khi ph i đóng cùng m tữ ụ ả ộ
lúc nhi u stream đang m . Nó s đóng t t c các stream và tr v s stream đã đóng ho c EOF n uề ở ẽ ấ ả ả ề ố ặ ế
có phát hi n l i. Nó có th đ c s d ng theo cách nh sau:ệ ỗ ể ượ ử ụ ư
fcl = fcloseall();
if (fcl == EOF)
printf("Error closing files");
else
printf("%d file(s) closed", fcl);
21.3.3 Ghi m t ký tộ ự
Streams có th đ c ghi vào t p tin theo t ng ký t m t ho c theo t ng chu i. Tr c h t chúng taể ượ ậ ừ ự ộ ặ ừ ỗ ướ ế
hãy th o lu n v cách ghi các ký t vào t p tin. Hàm ả ậ ề ự ậ fputc() đ c s d ng đ ghi các ký t vàoượ ử ụ ể ự
t p tin đã đ c m tr c đó b ng hàm ậ ượ ở ướ ằ fopen(). Nguyên m u c a hàm này nh sau:ẫ ủ ư
int fputc(int ch, FILE *fp);
trong đó fp là m t con tr t p tin tr v b i hàm ộ ỏ ậ ả ề ở fopen() và ch là ký t c n ghi. M c dù ự ầ ặ ch đ cượ
khai báo là ki u ểint, nh ng nó đ c hàm ư ượ fputc() chuy n đ i thành ki u ể ổ ể unsigned char. Hàm
fputc() ghi m t ký t vào stream đã đ nh t i v trí hi n hành c a con tr đ nh v trí bên trong t p tinộ ự ị ạ ị ệ ủ ỏ ị ị ậ
và sau đó tăng con tr này lên. N u ỏ ế fputc() thành công, nó tr v ký t đã ghi, ng c l i nó tr vả ề ự ượ ạ ả ề
EOF.
21.3.4 Đ c m t ký tọ ộ ự
Hàm fgetc() đ c dùng đ đ c các ký t t m t t p tin đã đ c m ch đ đ c, s d ng hàmượ ể ọ ự ừ ộ ậ ượ ở ở ế ộ ọ ử ụ
fopen(). Nguyên m u c a hàm là:ẫ ủ
32 L p trình c b n Cậ ơ ả

int fgetc (FILE *fp);
trong đó fp là m t con tr t p tin ki u FILE tr v b i hàm ộ ỏ ậ ể ả ề ở fopen(). Hàm fgetc() tr v ký t kả ề ự ế
ti p c a v trí hi n hành trong stream input, và tăng con tr đ nh v trí bên trong t p tin lên. Ký tế ủ ị ệ ỏ ị ị ậ ự
đ c đ c là m t ký t ki u ọ ượ ộ ự ể unsigned char và đ c chuy n thành ki u ượ ể ể int. N u đã đ n cu i t pế ế ố ậ
tin, fgetc() tr v EOF.ả ề
Đ đ c m t t p tin văn b n t đ u cho đ n cu i, câu l nh s là:ể ọ ộ ậ ả ừ ầ ế ố ệ ẽ
do
{
ch = fgetc(fp);
} while (ch != EOF);
Ch ng trình sau đây nh n các ký t t bàn phím và ghi chúng vào m t t p tin cho đ n khi ng iươ ậ ự ừ ộ ậ ế ườ
dùng nh p ký t ‘ậ ự @’. Sau khi ng i dùng nh p thông tin vào, ch ng trình s hi n th n i dung raườ ậ ươ ẽ ể ị ộ
màn hình.
Ví d 1:ụ
#include <stdio.h>
main()
{
FILE *fp;
char ch= ' ';
/* Writing to file JAK */
if ((fp=fopen("jak", "w"))==NULL)
{
printf("Cannot open file \n\n");
exit(1);
}
clrscr();
printf("Enter characters (type @ to terminate): \n");
ch = getche();
while (ch !='@')
{
fputc(ch, fp) ;
ch = getche();
}
fclose(fp);
/* Reading from file JAK */
printf("\n\nDisplaying contents of file JAK\n\n");
if((fp=fopen("jak", "r"))==NULL)
{
printf("Cannot open file\n\n");
exit(1);
}
do
{
ch = fgetc (fp);
putchar(ch) ;
} while (ch!=EOF);
Qu n lý t p tinả ậ 33

