- 在线时间
- 0 小时
- 最后登录
- 2007-9-23
- 注册时间
- 2004-9-10
- 听众数
- 3
- 收听数
- 0
- 能力
- 0 分
- 体力
- 9975 点
- 威望
- 7 点
- 阅读权限
- 150
- 积分
- 4048
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1893
- 主题
- 823
- 精华
- 2
- 分享
- 0
- 好友
- 0

我的地盘我做主
该用户从未签到
|
< align=left><FONT color=#cc0000><B>文件 C8 Z6 U! B, N* x1 ]. N5 S: t
9 ^4 R) H( ^' h; D
</B></FONT><FONT color=#ff0000>文件的基本概念</FONT>
; n0 e+ _( m# O! w 所谓“文件”是指一组相关数据的有序集合。 这个数据集有一个名称,叫做文件名。 实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。文件通常是驻留在外部介质(如磁盘等)上的, 在使用时才调入内存中来。从不同的角度可对文件作不同的分类。从用户的角度看,文件可分为普通文件和设备文件两种。
9 J# Q w0 ?$ `% G) t, E" j8 o$ J! b+ }1 |
普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序; 也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、 可执行程序可以称作程序文件,对输入输出数据可称作数据文件。
0 p2 |( Q3 _/ g' U7 g% I* k
+ o6 O S8 t" f5 V! t 设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。 通常把显示器定义为标准输出文件, 一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf,putchar 函数就是这类输出。键盘通常被指定标准的输入文件, 从键盘上输入就意味着从标准输入文件上输入数据。scanf,getchar函数就属于这类输入。
2 M* K. b+ _0 W/ M- I
& B$ T, z5 Q7 L 从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。
, u9 p/ C2 }# v6 e4 D/ r, a+ \3 n- H; }% Q; p! N# j4 a
ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:
+ ?: m7 P( ?3 {% ~) q- a$ EASC码: 00110101 00110110 00110111 001110001 f/ Z' n1 c" ~, F0 H
↓ ↓ ↓ ↓: L# E$ o3 R0 S$ ~% W8 H
十进制码: 5 6 7 8 共占用4个字节。ASCII码文件可在屏幕上按字符显示, 例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。 由于是按字符显示,因此能读懂文件内容。$ Z' i1 v, e, M6 m
; m. a8 H m: v 二进制文件是按二进制的编码方式来存放文件的。 例如, 数5678的存储形式为: 00010110 00101110只占二个字节。二进制文件虽然也可在屏幕上显示, 但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。 输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。 因此也把这种文件称作“流式文件”。
9 [1 |5 U5 Q- \ P2 G6 s& e5 ?( B" ?8 p% Q
本章讨论流式文件的打开、关闭、读、写、 定位等各种操作。文件指针在C语言中用一个指针变量指向一个文件, 这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。 定义说明文件指针的一般形式为: FILE* 指针变量标识符; 其中FILE应为大写,它实际上是由系统定义的一个结构, 该结构中含有文件名、文件状态和文件当前位置等信息。 在编写源程序时不必关心FILE结构的细节。例如:FILE *fp; 表示fp是指向FILE结构的指针变量,通过fp 即可找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件, 实施对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。文件的打开与关闭文件在进行读写操作之前要先打开,使用完毕要关闭。 所谓打开文件,实际上是建立文件的各种有关信息, 并使文件指针指向该文件,以便进行其它操作。关闭文件则断开指针与文件之间的联系,也就禁止再对该文件进行操作。
' f# P z. P, Q4 E* _ w; @' T8 T
4 k7 ?: c1 y1 N/ j5 h+ V/ ~ 在C语言中,文件操作都是由库函数来完成的。 在本章内将介绍主要的文件操作函数。
1 c9 D, u8 P8 s, m* L# y4 d+ _5 n/ b* ~0 ?% P4 Z1 s5 y
<FONT color=#ff0000>文件打开函数fopen</FONT>
9 V, C8 |2 o3 q* c/ w7 L9 `; h+ [' v0 d% R. C
fopen函数用来打开一个文件,其调用的一般形式为: 文件指针名=fopen(文件名,使用文件方式) 其中,“文件指针名”必须是被说明为FILE 类型的指针变量,“文件名”是被打开文件的文件名。 “使用文件方式”是指文件的类型和操作要求。“文件名”是字符串常量或字符串数组。例如:
" `/ ~8 H# m. `: X) c<FONT color=#009900>FILE *fp;
% e# l6 r$ Y7 O) q- L ]fp=("file a","r");</FONT>
9 K: C! F) H8 h- P. T其意义是在当前目录下打开文件file a, 只允许进行“读”操作,并使fp指向该文件。
$ L: K* I# E8 l6 f又如:* i/ E7 i5 K8 g7 ^: u3 {. E) U
<FONT color=#009900>FILE *fphzk- a' P, F- T8 V$ A1 N8 \, K
fphzk=("c:\\hzk16',"rb")</FONT>
/ A4 T, b3 c. H$ Z其意义是打开C驱动器磁盘的根目录下的文件hzk16, 这是一个二进制文件,只允许按二进制方式进行读操作。两个反斜线“\\ ”中的第一个表示转义字符,第二个表示根目录。使用文件的方式共有12种,下面给出了它们的符号和意义。 * o) q" \: q6 b/ @$ w" S
文件使用方式 意 义$ b( @/ ?, W9 O( p. i
<FONT color=#009900>“rt” 只读打开一个文本文件,只允许读数据
8 z$ Y7 V0 R+ q- q- M/ F. E0 c“wt” 只写打开或建立一个文本文件,只允许写数据/ C/ H# W& p$ f
“at” 追加打开一个文本文件,并在文件末尾写数据( E3 V! U4 r" h; N) X, B# Z( k* k
“rb” 只读打开一个二进制文件,只允许读数据4 Z6 V8 k" N- O U7 A% l6 `- y
“wb” 只写打开或建立一个二进制文件,只允许写数据5 w9 g- K7 M# e& x% U- s; a
“ab” 追加打开一个二进制文件,并在文件末尾写数据6 c1 K- w# k& w2 }/ H# K
“rt+” 读写打开一个文本文件,允许读和写
' S5 ^( K2 Y9 d# w& f. M“wt+” 读写打开或建立一个文本文件,允许读写) y- y, R8 i$ F) J
“at+” 读写打开一个文本文件,允许读,或在文件末追加数 据
, {( T' m2 F" V) n“rb+” 读写打开一个二进制文件,允许读和写
9 v! L: `9 ?2 D/ ]7 d“wb+” 读写打开或建立一个二进制文件,允许读和写
2 F8 Y) E' \8 F3 C# J0 t“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据& P. j$ E" z# y" \" n
</FONT>0 e C* A" v) Q
对于文件使用方式有以下几点说明:3 S x- ]$ k$ Z( e* a' ?. n
1. 文件使用方式由r,w,a,t,b,+六个字符拼成,各字符的含义是:( P2 u: e4 I5 M7 S# u* S/ p
r(read): 读0 C( p$ e q/ F, U4 e) `7 V* S
w(write): 写
4 @$ t: p# U! F! R# U6 H! C. N: {a(append): 追加/ j' r: C: G( a; ]& m' y. O; Q1 r
t(text): 文本文件,可省略不写, n3 {: }' I' \- |4 z
b(banary): 二进制文件+ _* i, X/ C/ p1 Q
+: 读和写
1 o. i. K$ f6 N- k# ]) E& F% R. I8 W" j7 ~4 C6 |9 J/ W5 f5 [
2. 凡用“r”打开一个文件时,该文件必须已经存在, 且只能从该文件读出。
: {3 g6 Z& U* F, V& b8 ?- O' ~+ [% P# I# ]# F$ C$ \, A8 d
3. 用“w”打开的文件只能向该文件写入。 若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件。9 g$ J' y% |# \
! n) e% P$ _: x9 J) i
4. 若要向一个已存在的文件追加新的信息,只能用“a ”方式打开文件。但此时该文件必须是存在的,否则将会出错。
3 X, a9 a0 l( i5 H; R% m- i& l. n+ @0 U) ]+ R7 n: \* T
5. 在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。因此常用以下程序段打开文件:
/ S0 ~$ O# E+ K. h6 C, ?<FONT color=#009900>if((fp=fopen("c:\\hzk16","rb")==NULL)9 j; ]: f% @# X4 I) B
{
# h# Q8 S8 i8 r; R; U iprintf("\nerror on open c:\\hzk16 file!");
, A9 N: h1 A- z Ogetch();* `6 O6 q# L' b2 H& n
exit(1);
; A/ G+ `, w Y6 ~1 @9 b& W# d}% [+ z! m( ?$ N4 m" G
</FONT> 这段程序的意义是,如果返回的指针为空,表示不能打开C盘根目录下的hzk16文件,则给出提示信息“error on open c:\ hzk16file!”,下一行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里,该行的作用是等待, 只有当用户从键盘敲任一键时,程序才继续执行, 因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。
" f. U, S$ t# Q0 ]) b# d! G7 x# z
6. 把一个文本文件读入内存时,要将ASCII码转换成二进制码, 而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。, W3 }, ^- Q' r; a/ M5 q
' N( J4 y" j+ G$ ~% {
7. 标准输入文件(键盘),标准输出文件(显示器 ),标准出错输出(出错信息)是由系统打开的,可直接使用。文件关闭函数fclose文件一旦使用完毕,应用关闭文件函数把文件关闭, 以避免文件的数据丢失等错误。
% I1 b, A6 r& P% X; `/ J, c
: C+ b* p) d; z1 T+ {$ o<FONT color=#ff0000>fclose函数5 D7 N1 F v- O0 D6 L* ~
4 F4 T ^$ y6 J' [5 a% K% F8 s</FONT>调用的一般形式是: fclose(文件指针); 例如:
- p$ x) j) T2 L7 L, o3 d- e! e% yfclose(fp); 正常完成关闭文件操作时,fclose函数返回值为0。如返回非零值则表示有错误发生。文件的读写对文件的读和写是最常用的文件操作。
. n3 L7 F# v6 t& n9 e+ I, R$ s
/ w/ s2 N( v( }2 B* q7 S8 X在C语言中提供了多种文件读写的函数:
1 I+ t/ \: \: t) ~$ n·字符读写函数 :fgetc和fputc; a: H- u: R. O8 n4 }
·字符串读写函数:fgets和fputs
0 @9 u: j; @2 ]' L·数据块读写函数:freed和fwrite$ T( J& n) I" p' P* L4 P
·格式化读写函数:fscanf和fprinf0 o4 F: z8 W$ M/ |
& U/ w. i0 v/ o3 A3 q 下面分别予以介绍。使用以上函数都要求包含头文件stdio.h。字符读写函数fgetc和fputc字符读写函数是以字符(字节)为单位的读写函数。 每次可从文件读出或向文件写入一个字符。4 K. W# e# v. i& F! M7 W
/ H2 d# J# K1 a6 m; j<FONT color=#ff0000>一、读字符函数fgetc</FONT>
! j$ ~' {* `, S; e$ M- H9 o8 E2 J3 V' v
fgetc函数的功能是从指定的文件中读一个字符,函数调用的形式为: 字符变量=fgetc(文件指针); 例如:ch=fgetc(fp);其意义是从打开的文件fp中读取一个字符并送入ch中。& Y3 g! i, _0 b- K8 Q& Z2 ~
+ k; |0 Z7 q9 u" d 对于fgetc函数的使用有以下几点说明:
- G3 ?/ k; d( |1. 在fgetc函数调用中,读取的文件必须是以读或读写方式打开的。
7 b7 t1 A0 [5 @9 U
8 i; d4 E- u. p6 ?8 y2. 读取字符的结果也可以不向字符变量赋值,例如:fgetc(fp);但是读出的字符不能保存。
- b1 R4 x- P8 A9 m+ c* C* ^5 p8 }) Q1 A. t
3. 在文件内部有一个位置指针。用来指向文件的当前读写字节。在文件打开时,该指针总是指向文件的第一个字节。使用fgetc 函数后, 该位置指针将向后移动一个字节。 因此可连续多次使用fgetc函数,读取多个字符。 应注意文件指针和文件内部的位置指针不是一回事。文件指针是指向整个文件的,须在程序中定义说明,只要不重新赋值,文件指针的值是不变的。文件内部的位置指针用以指示文件内部的当前读写位置,每读写一次,该指针均向后移动,它不需在程序中定义说明,而是由系统自动设置的。( n$ V/ d7 O- r4 i
# Q2 m) a# q- G) l) l<FONT color=#ff00ff><B>[例10.1]</B></FONT>读入文件e10-1.c,在屏幕上输出。5 U4 ~3 ]6 i1 C, I
<FONT color=#009900>#include<stdio.h>
+ \9 K+ m1 ?: Hmain()
4 l0 Q! D/ u1 C4 w) _{. s; u% o" m$ Z1 H2 [3 r. L
FILE *fp;
, R+ z* o1 X7 a* E7 ]char ch;
+ N: V4 ?$ N( R8 Y* z( p/ Sif((fp=fopen("e10_1.c","rt"))==NULL)
+ p/ D: S9 S' N' j6 H3 D{8 k! W& b% b/ ?, Q8 S
printf("Cannot open file strike any key exit!");8 r4 |/ s1 W/ A* c7 B
getch();+ n4 G |$ I8 v
exit(1);
# K \7 ^: `; Z: n. W. A% C}
# M; R# g; l; K* nch=fgetc(fp);
" ~6 ?3 |1 y- P/ z$ ?while (ch!=EOF)
3 f0 m5 o4 P- h( k{
, [) Z2 T5 q- q% e, p6 Mputchar(ch);! m1 Y* W f, D4 Z% }2 L: V
ch=fgetc(fp);
( T* P6 V" f) Z}
' ^. e& ]% ]' W+ y8 ^* {fclose(fp);
9 E0 v5 i' e1 H4 f2 p) B}</FONT>, v1 Q, y0 S* m2 K! s( _) A: L+ m
本例程序的功能是从文件中逐个读取字符,在屏幕上显示。 程序定义了文件指针fp,以读文本文件方式打开文件“e10_1.c”, 并使fp指向该文件。如打开文件出错, 给出提示并退出程序。程序第12行先读出一个字符,然后进入循环, 只要读出的字符不是文件结束标志(每个文件末有一结束标志EOF)就把该字符显示在屏幕上,再读入下一字符。每读一次,文件内部的位置指针向后移动一个字符,文件结束时,该指针指向EOF。执行本程序将显示整个文件。
4 ?' b8 v! r0 j# t2 J1 @, p0 p% W3 h9 G) k+ C8 ]
<FONT color=#ff0000>二、写字符函数fputc</FONT>! U, J# D' j1 g2 K( \) z
4 }6 l9 D% j3 t3 T4 m% ^% k w fputc函数的功能是把一个字符写入指定的文件中,函数调用的 形式为: fputc(字符量,文件指针); 其中,待写入的字符量可以是字符常量或变量,例如:fputc('a',fp);其意义是把字符a写入fp所指向的文件中。
) K8 g9 C% {- l/ ^
( X( g) d" e4 C4 [4 L$ a 对于fputc函数的使用也要说明几点:$ Z% z% c# c [' B$ [+ P
1. 被写入的文件可以用、写、读写,追加方式打开,用写或读写方式打开一个已存在的文件时将清除原有的文件内容,写入字符从文件首开始。如需保留原有文件内容,希望写入的字符以文件末开始存放,必须以追加方式打开文件。被写入的文件若不存在,则创建该文件。( h0 U! D0 O+ _: U; I
1 w) e8 F% m" S( r% m2. 每写入一个字符,文件内部位置指针向后移动一个字节。! \/ s* c+ r, A. I* e! g1 {
2 `' S! @# W% Q: G
3. fputc函数有一个返回值,如写入成功则返回写入的字符, 否则返回一个EOF。可用此来判断写入是否成功。9 j8 U* @5 C, }' C
3 g0 b% Z5 a8 O8 f% \2 z, V& ] G<FONT color=#ff00ff><B>[例10.2]</B></FONT>从键盘输入一行字符,写入一个文件, 再把该文件内容读出显示在屏幕上。% j9 C0 Z5 Y, G; D* Y
<FONT color=#009900>#include<stdio.h>4 A2 W# {: x. t
main()! c; `: B0 _. ~" @
{' v0 S f# [ n0 [7 T3 O) z/ D9 U
FILE *fp;0 u1 w8 B! P# ^: i) K2 ^, ]
char ch;
) Q/ P; z! B, }) Y* cif((fp=fopen("string","wt+"))==NULL)
# j- Y4 A# X& u{
8 V' u. G* T" f/ S. X7 z, N% i+ W: |printf("Cannot open file strike any key exit!");% J* ?$ \7 D; n9 J% q5 n I0 Y
getch();
) V1 }/ Y8 W$ u- i" jexit(1);, `8 ]3 L& S5 N% z( q) l8 y
}
$ M) z* ?7 f$ S0 B3 ^4 F" K( iprintf("input a string:\n");+ D6 Q' }$ B5 A' G9 K
ch=getchar();) }0 j& M: b0 N# }3 u
while (ch!='\n')
# K T* O; f8 Z( y# ?{8 i, v4 T7 B& @( G* U
fputc(ch,fp);
1 v7 u$ d# f; l1 W* A* F8 V6 hch=getchar();7 w& g" b3 r. ^/ t3 A
}
4 N: s: |) S0 J8 Y# X3 Erewind(fp);
- g) F, s' z* V$ jch=fgetc(fp);6 m" D. P4 c- B' r% ~
while(ch!=EOF)
' v$ H" p6 K. p6 ? b4 ]9 A* c{; _# r/ p4 T* M1 _. G& [
putchar(ch);
. k' i* ^: }* d# x$ M" u- g- u9 `ch=fgetc(fp);( y; E o+ Y* h9 T" c) @6 x. L0 _
} M. u; B9 u( Y6 W% n% y
printf("\n");5 u* l8 A( r! U
fclose(fp);
7 s( K* d! r2 V}</FONT>+ C6 |- s2 \" B- Z1 U' w* S
程序中第6行以读写文本文件方式打开文件string。程序第13行从键盘读入一个字符后进入循环,当读入字符不为回车符时, 则把该字符写入文件之中,然后继续从键盘读入下一字符。 每输入一个字符,文件内部位置指针向后移动一个字节。写入完毕, 该指针已指向文件末。如要把文件从头读出,须把指针移向文件头, 程序第19行rewind函数用于把fp所指文件的内部位置指针移到文件头。 第20至25行用于读出文件中的一行内容。
1 C# x- r3 O9 R. Z& u. H
/ g# d4 @: m5 A6 Z# F3 M<FONT color=#ff00ff><B>[例10.3]</B></FONT>把命令行参数中的前一个文件名标识的文件, 复制到后一个文件名标识的文件中, 如命令行中只有一个文件名则把该文件写到标准输出文件(显示器)中。/ J& f6 A+ N5 ?( U% J M
<FONT color=#009900>#include<stdio.h>
/ p/ D- z+ b1 W' w- l' n4 T$ ]main(int argc,char *argv[])
# _3 ]8 g. M, N( ^{
: z; F2 e& l- s( k( p7 Z) p: hFILE *fp1,*fp2;4 `( Y3 K( j/ r2 ?
char ch;
# D( G( l. d: Rif(argc==1)
; {$ D! b# W; v3 r{+ t( L w" O4 o" ]
printf("have not enter file name strike any key exit");) {6 t0 k7 q& R; f
getch();
! g3 Y$ o" M7 Y2 x* W1 U, ^exit(0);! Q/ f' q5 a9 q( d7 q
}0 t3 L* y+ y! @' U- [5 C4 p" x
if((fp1=fopen(argv[1],"rt"))==NULL)" _: `9 h T# l0 W0 ~: m9 r: e. d. e
{4 e! c, }/ ^4 C- F' Y' e+ }
printf("Cannot open %s\n",argv[1]);
* l5 N" k$ c! N; {7 o' {getch();
( I; X- T, H% D0 N) @5 Cexit(1);
2 \4 B3 v7 U7 f}" R6 s& C' j# T# n# I$ d: [ |
if(argc==2) fp2=stdout;
' B& T! K, O8 _else if((fp2=fopen(argv[2],"wt+"))==NULL)- \2 R: H) k w" b) B2 m; g
{5 V! G# g/ o, t; l( V
printf("Cannot open %s\n",argv[1]);) K9 j Z+ g8 ~* @$ |
getch();/ L7 X6 \/ m5 o4 u6 [3 l
exit(1);
7 L1 O" o- D, F# E: ]}# s0 P' s! u: U, B' B
while((ch=fgetc(fp1))!=EOF)
( }- ]0 r( N! M! s4 a) n2 g+ Ofputc(ch,fp2);
3 `+ n' ^0 c# a7 b# `& Kfclose(fp1);# Z2 p4 `. M$ ]" c8 B) r; c
fclose(fp2);
9 G5 H. x' p, X5 r3 h5 ^0 f9 K* N}</FONT>
# U8 }, C% b- s) B+ l0 L% T: ?# D 本程序为带参的main函数。程序中定义了两个文件指针 fp1 和fp2,分别指向命令行参数中给出的文件。如命令行参数中没有给出文件名,则给出提示信息。程序第18行表示如果只给出一个文件名,则使fp2指向标准输出文件(即显示器)。程序第25行至28行用循环语句逐个读出文件1中的字符再送到文件2中。再次运行时,给出了一个文件名(由例10.2所建立的文件), 故输出给标准输出文件stdout,即在显示器上显示文件内容。第三次运行,给出了二个文件名,因此把string中的内容读出,写入到OK之中。可用DOS命令type显示OK的内容:字符串读写函数fgets和fputs+ s$ `$ C! I( {" _, K& a! i
8 o' K2 D" K, l# N' M! Z0 m一、<FONT color=#ff0000>读字符串函数fgets函数</FONT>的功能是从指定的文件中读一个字符串到字符数组中,函数调用的形式为: fgets(字符数组名,n,文件指针); 其中的n是一个正整数。表示从文件中读出的字符串不超过 n-1个字符。在读入的最后一个字符后加上串结束标志'\0'。例如:fgets(str,n,fp);的意义是从fp所指的文件中读出n-1个字符送入字符数组str中。9 J) F# | f# @
<B><FONT color=#ff00ff>[例10.4]</FONT></B>从e10_1.c文件中读入一个含10个字符的字符串。
& [+ _0 t5 q7 f& ~ j7 B( z: m3 M7 n<FONT color=#009900>#include<stdio.h>7 j+ O9 w9 [6 B2 D- l
main()
, u* u; B8 @ i) h. k* u! k3 V{) q$ }( l- c0 h" t5 ^6 l& B
FILE *fp;
( h* {# W C' J0 v! d' A+ q7 Ychar str[11];& M0 {) V5 k/ O3 Z
if((fp=fopen("e10_1.c","rt"))==NULL)
0 `( w. x! j2 u: i- m{
G9 T( W: @) lprintf("Cannot open file strike any key exit!");
+ a3 p3 H2 g# v+ q# x7 ?getch();
5 R" G* N' [& |' w$ {/ T1 N: h1 Iexit(1);" R! B) k0 k$ s) T
}
1 p" |$ ^+ I. }% E' ]: w+ Xfgets(str,11,fp);
1 r/ d) ^* \& hprintf("%s",str);
% l& V3 N% k, X/ e, |fclose(fp);
% r* N5 K) v% R% P0 }6 M& s}</FONT>
$ }7 ?* ?3 w! ^) M9 @ 本例定义了一个字符数组str共11个字节,在以读文本文件方式打开文件e101.c后,从中读出10个字符送入str数组,在数组最后一个单元内将加上'\0',然后在屏幕上显示输出str数组。输出的十个字符正是例10.1程序的前十个字符。
# D4 o1 C8 G! Y. F, T! [2 C. T. f9 o& _
对fgets函数有两点说明:
* J1 [* A% p, i4 f+ J- |1. 在读出n-1个字符之前,如遇到了换行符或EOF,则读出结束。
4 a$ D! ?9 A. e2 i2. fgets函数也有返回值,其返回值是字符数组的首地址。- ^. e8 Y( d3 I, L; |
) L8 K1 g' y: o; L" @
<FONT color=#ff0000>二、写字符串函数fputs</FONT>
$ S) d6 `$ O5 \1 M
) x m2 H/ e1 z" r, _8 ~6 ofputs函数的功能是向指定的文件写入一个字符串,其调用形式为: fputs(字符串,文件指针) 其中字符串可以是字符串常量,也可以是字符数组名, 或指针 变量,例如: T5 P% p9 O) O1 @- V/ P
fputs(“abcd“,fp);
5 B9 K# e8 C+ ?7 X4 n- X其意义是把字符串“abcd”写入fp所指的文件之中。[例10.5]在例10.2中建立的文件string中追加一个字符串。) S) e" ]6 W5 g1 t* S; S2 T
<FONT color=#009900>#include<stdio.h>& K* M1 S! q H* ?3 u! n
main()
6 N- W/ |3 W/ h# H! v% y{
/ m7 f/ p+ X! ^' A2 s6 UFILE *fp;
7 d0 d4 D' K3 _1 F: A- [' a1 l% Schar ch,st[20]; f% B! v; T+ E
if((fp=fopen("string","at+"))==NULL)
5 c9 q: x) g, U! v3 t* _{3 G; a" M& l: @: i9 |9 L. r
printf("Cannot open file strike any key exit!");
1 g7 E+ e+ ]2 K; [" A4 S( ogetch();
Q; R F+ G8 q' {exit(1);
6 j. v/ ~) c. N+ f) a}
& z2 g3 ]. Y% l. ?4 O* gprintf("input a string:\n");2 R; l3 n, [: F8 n/ P" ]7 ~7 f
scanf("%s",st);
# Z0 ?0 c( l: wfputs(st,fp);, F1 J( Z; o a" q O
rewind(fp);8 _! x, I) O) m9 b8 ]
ch=fgetc(fp);
6 m( m* h3 ?- i! L S: R( |+ T! swhile(ch!=EOF)
7 T' @4 h( k$ M/ R{7 H! ]2 T8 j- J5 t8 Q& T
putchar(ch);; y) e3 Q/ Y& G# `/ X0 e G8 d. T
ch=fgetc(fp);5 {8 E, T8 ~& n0 J
}5 t O! c+ z/ L! f) n
printf("\n");
3 v' M) i; ?( D# K& v1 F- mfclose(fp);7 S8 k6 J2 b2 m
}</FONT># p. V9 Y6 s/ J U
本例要求在string文件末加写字符串,因此,在程序第6行以追加读写文本文件的方式打开文件string 。 然后输入字符串, 并用fputs函数把该串写入文件string。在程序15行用rewind函数把文件内部位置指针移到文件首。 再进入循环逐个显示当前文件中的全部内容。; l9 t8 P) _& K, R% W
4 N( }. P" m2 j& F2 ~3 Z<FONT color=#ff0000>数据块读写函数fread和fwrite</FONT>
4 o4 B" R5 v$ C7 M
+ {# g& ?+ p! G5 D( }' f5 w C语言还提供了用于整块数据的读写函数。 可用来读写一组数据,如一个数组元素,一个结构变量的值等。读数据块函数调用的一般形式为: fread(buffer,size,count,fp); 写数据块函数调用的一般形式为: fwrite(buffer,size,count,fp); 其中buffer是一个指针,在fread函数中,它表示存放输入数据的首地址。在fwrite函数中,它表示存放输出数据的首地址。 size 表示数据块的字节数。count 表示要读写的数据块块数。fp 表示文件指针。
# P7 X5 Z+ l/ ~例如:7 R% L7 a2 Q& K( n
fread(fa,4,5,fp); 其意义是从fp所指的文件中,每次读4个字节(一个实数)送入实数组fa中,连续读5次,即读5个实数到fa中。
Q: D$ ]1 {/ F4 F, Y+ @( a% w0 `[例10.6]从键盘输入两个学生数据,写入一个文件中, 再读出这两个学生的数据显示在屏幕上。
" v( x+ `: j* I6 ~, w<FONT color=#009900>#include<stdio.h>: ~. K: T& P, y8 M% ^
struct stu
4 i" v# F1 | F" ^ c5 E% C{; {, J, c: w/ B, j% T
char name[10];, E, P& {9 @8 q
int num;
+ D& o! ~1 k/ U! I3 O1 ?int age;( o9 u& @! T4 z$ y
char addr[15];2 N. y; s: c6 m3 a
}boya[2],boyb[2],*pp,*qq;
9 y$ _2 e; d* ^9 W4 ymain()# m2 y# l& g" T- k( l- A1 y# b
{
! Y" d& }0 s8 A( c% C* _FILE *fp;+ P* D. U% Y0 Y7 E) W6 o$ z. y
char ch;
' y- I" l/ P2 }, c. Qint i;$ M/ K- e/ ~5 _' G2 V! v
pp=boya;
5 h2 D3 Y( D" {1 E: A* E8 Sqq=boyb;3 x8 L; i2 i9 ~5 j- E$ F4 e
if((fp=fopen("stu_list","wb+"))==NULL)
; \0 L6 c2 R0 X{. ~/ U" E: {/ G @" D
printf("Cannot open file strike any key exit!");6 ?. \& L/ B1 l: _
getch();
0 |) x! H5 d* B" z3 fexit(1);+ U2 N2 [- s9 S }
}7 z! C0 S$ D3 Y ?
printf("\ninput data\n");$ q8 b* L' J+ W2 C" y8 C
for(i=0;i<2;i++,pp++)
0 |+ K8 ]& @2 _0 M4 ~4 W; cscanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);
J1 p3 H) B! j) opp=boya;$ h; E/ W! x# q. |' U0 D
fwrite(pp,sizeof(struct stu),2,fp);
. }: n N; N: u2 _7 l5 y% R/ H1 @1 [: \rewind(fp);8 _# _6 `/ F+ C& q% E0 O! k
fread(qq,sizeof(struct stu),2,fp);
+ d- G! Y/ f, hprintf("\n\nname\tnumber age addr\n");- X$ @9 `5 \; {% N/ h
for(i=0;i<2;i++,qq++)9 x1 o! I+ @; T" c4 l
printf("%s\t%5d%7d%s\n",qq->name,qq->num,qq->age,qq->addr);
. D; A/ I# `2 M8 Ffclose(fp);8 f- e. E2 b Y7 Z3 F5 P- F8 E2 m4 G
}</FONT>
+ l& t3 I. ~) Z; l# k% j& | 本例程序定义了一个结构stu,说明了两个结构数组boya和 boyb以及两个结构指针变量pp和qq。pp指向boya,qq指向boyb。程序第16行以读写方式打开二进制文件“stu_list”,输入二个学生数据之后,写入该文件中, 然后把文件内部位置指针移到文件首,读出两块学生数据后,在屏幕上显示。
8 B5 r" Z5 B% i, ?# f7 C4 v/ F+ J- C8 t9 N* Q
<FONT color=#ff0000>格式化读写函数fscanf和fprintf</FONT>
5 F' _# }+ v0 b$ [: n! s
" @" G2 _6 J" b, vfscanf函数,fprintf函数与前面使用的scanf和printf 函数的功能相似,都是格式化读写函数。 两者的区别在于 fscanf 函数和fprintf函数的读写对象不是键盘和显示器,而是磁盘文件。这两个函数的调用格式为: fscanf(文件指针,格式字符串,输入表列); fprintf(文件指针,格式字符串,输出表列); 例如:
6 W& @. h. x+ q3 }3 `fscanf(fp,"%d%s",&i,s);1 `$ |9 p, [2 x6 Y, v ^
fprintf(fp,"%d%c",j,ch);
2 f8 N2 r; _$ ^* L% t4 b. g用fscanf和fprintf函数也可以完成例10.6的问题。修改后的程序如例10.7所示。
/ h, {9 G5 E6 D3 a' [" v[例10.7], }: ]* x9 j# O
<FONT color=#009900>#include<stdio.h>
* A- ^) \- h7 v& [1 Estruct stu
5 Y8 e. \" _6 Y- e- y0 l& m{
8 ], V+ D+ D+ `& P$ Schar name[10];
, M+ V x, |' v2 @0 \, N4 b' jint num;
' @8 o; G1 L( W* N, Cint age;
8 x, C6 ^% Q& P0 h* fchar addr[15];8 k9 _) C) X: @' p' F/ r9 L' ~3 o
}boya[2],boyb[2],*pp,*qq;
5 }0 }# @9 U) {! `+ f9 _main()
% n- `& y+ p j' g+ ?& Q{
$ c7 r& ~5 \7 j. k4 _/ \! pFILE *fp;
' [+ S+ {" Q" [% n3 Tchar ch;
3 _$ ]" r1 ?7 {5 P- Iint i;
. T) s( p2 H$ d4 t4 Tpp=boya;& p1 q% o' q5 \: n$ |$ g
qq=boyb;) _8 X: [9 s' q2 i1 H& w$ e6 @
if((fp=fopen("stu_list","wb+"))==NULL)
. z$ v) _2 o9 W- J- I7 d% Z' E{' @9 ]9 L9 i8 l' B
printf("Cannot open file strike any key exit!");# [; N# G3 B' n0 J
getch();& H w$ |' A4 [! f5 D2 E
exit(1);1 z" Y9 ~ m( [. G! p
}' z# M( y# n, z4 c% S
printf("\ninput data\n");* l' e3 j" [( e8 g% R
for(i=0;i<2;i++,pp++)
2 V! Q9 S9 T( H8 z; yscanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);
d. R& y8 J) h. Y7 q/ w+ ppp=boya;6 t9 D$ t' j% j# k$ _8 E* n; ] e
for(i=0;i<2;i++,pp++)9 }" t# A$ T; E0 n2 b( [( t3 b) x& b
fprintf(fp,"%s %d %d %s\n",pp->name,pp->num,pp->age,pp->" O) m$ K8 W! n! |) x/ `
addr);
4 g: z* C, J* a# o8 U7 O `rewind(fp);
# n, p4 ?1 J+ F7 W# X1 g+ k: h8 vfor(i=0;i<2;i++,qq++)
$ G# @* G' D6 _/ G3 Y$ jfscanf(fp,"%s %d %d %s\n",qq->name,&qq->num,&qq->age,qq->addr);
: p' q' f9 y5 Y& Y# s( W# W, @% tprintf("\n\nname\tnumber age addr\n");1 p3 |8 }% i& G6 `. y
qq=boyb;
h* n. l, e J. U8 _# Ufor(i=0;i<2;i++,qq++)
6 K$ w2 d' K% B% q w( O+ V9 @printf("%s\t%5d %7d %s\n",qq->name,qq->num, qq->age,% x# d# ^ L6 }- a2 A
qq->addr); q, o* p" a0 X" L* |
fclose(fp);" I C0 M+ s! N4 t
}</FONT>
, ]2 r/ s! {. j+ M6 B& \1 E 与例10.6相比,本程序中fscanf和fprintf函数每次只能读写一个结构数组元素,因此采用了循环语句来读写全部数组元素。 还要注意指针变量pp,qq由于循环改变了它们的值,因此在程序的25和32行分别对它们重新赋予了数组的首地址。( G9 b$ }2 J; F
/ d m- \; u- Q. q' k7 B<FONT color=#ff0000>文件的随机读写</FONT>9 A# P) r+ X; @* ]* p
( }5 C" a$ d0 z% D; F 前面介绍的对文件的读写方式都是顺序读写, 即读写文件只能从头开始,顺序读写各个数据。 但在实际问题中常要求只读写文件中某一指定的部分。 为了解决这个问题可移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写。 实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。文件定位移动文件内部位置指针的函数主要有两个, 即 rewind 函数和fseek函数。! Q" b* Y& u2 Q% r. t2 B+ m6 M
% F) Y$ r) m; A% b2 Q. N
rewind函数前面已多次使用过,其调用形式为: rewind(文件指针); 它的功能是把文件内部的位置指针移到文件首。 下面主要介绍; G* ~' g# i; W: A+ F7 ^$ C
fseek函数。
/ d- Q) p' S) q! w. X2 ?# o: Y& @% D
fseek函数用来移动文件内部位置指针,其调用形式为: fseek(文件指针,位移量,起始点); 其中:“文件指针”指向被移动的文件。 “位移量”表示移动的字节数,要求位移量是long型数据,以便在文件长度大于64KB 时不会出错。当用常量表示位移量时,要求加后缀“L”。“起始点”表示从何处开始计算位移量,规定的起始点有三种:文件首,当前位置和文件尾。
3 q2 ~$ H& @# @其表示方法如表10.2。
7 ~$ A2 {- ~( k& t8 s起始点 表示符号 数字表示( n) f" M `+ c$ V' B
──────────────────────────9 u6 `! h" u* ]( U; I F
文件首 SEEK—SET 06 _( E' O8 L1 c4 ~9 M
当前位置 SEEK—CUR 1
% u, W ]' H$ p. o7 s& l# c- n* }文件末尾 SEEK—END 2
3 \6 \- v) U7 Y5 w8 C例如:
, S" I+ ~' y/ G0 W/ @. f5 P& rfseek(fp,100L,0);其意义是把位置指针移到离文件首100个字节处。还要说明的是fseek函数一般用于二进制文件。在文本文件中由于要进行转换,故往往计算的位置会出现错误。文件的随机读写在移动位置指针之后, 即可用前面介绍的任一种读写函数进行读写。由于一般是读写一个数据据块,因此常用fread和fwrite函数。下面用例题来说明文件的随机读写。
$ ~' L+ o) ], I* |% l
7 C! w* S& X4 v" i7 y0 e3 u<FONT color=#ff00ff><B>[例10.8]</B></FONT>在学生文件stu list中读出第二个学生的数据。$ I$ {( O9 [' A4 o3 G" _
<FONT color=#009900>#include<stdio.h>$ ^6 I, K3 m3 ]6 X O( q
struct stu
5 V: ^+ J3 H- {' k' k1 ~% w{# Q% [" D9 {, n5 r7 Y
char name[10];* [9 k ]' }& O
int num;& e( U( ?5 G9 S" p0 e' Z
int age;6 e3 N+ Z% @3 X; y) R) ^8 e
char addr[15];, J, W2 P. K0 a" t6 ~
}boy,*qq;! Q7 ~* g# X- Q8 b) `1 D
main()
7 U* C2 C* c& @{% ^( w x" I. _5 R9 _9 A% `: s
FILE *fp;
6 T8 k9 {! r1 |7 Fchar ch;) @ k) [, y: ]9 T
int i=1;
0 }. j9 K& z7 K$ X* d$ Sqq=&boy;
/ k% x4 f- S2 u' W) u1 V! d3 Hif((fp=fopen("stu_list","rb"))==NULL)
( K- z. b/ Y7 M9 @0 c* i+ N" {{+ l/ Y/ }# k" L5 X$ s
printf("Cannot open file strike any key exit!");8 M6 }: i( B$ A6 `. I5 H6 d
getch();
& @3 o8 i9 a' [" W- w7 mexit(1);
! p7 v% W$ O/ C, | a- N}
% W4 o4 e% B1 b& ~rewind(fp);$ c! N( M( X* v7 N
fseek(fp,i*sizeof(struct stu),0);5 _3 M5 k( L: Q. p' C* [- v
fread(qq,sizeof(struct stu),1,fp);
+ {% A& H- u) K# Iprintf("\n\nname\tnumber age addr\n");0 r$ W8 {3 f7 C) f2 k
printf("%s\t%5d %7d %s\n",qq->name,qq->num,qq->age,
7 B( D& s$ R( ~qq->addr);
0 Y R+ |2 `' \/ ], L}</FONT>
$ K3 [, T" B1 V0 W7 A8 u6 v) u" U 文件stu_list已由例10.6的程序建立,本程序用随机读出的方法读出第二个学生的数据。程序中定义boy为stu类型变量,qq为指向boy的指针。以读二进制文件方式打开文件,程序第22行移动文件位置指针。其中的i值为1,表示从文件头开始,移动一个stu类型的长度, 然后再读出的数据即为第二个学生的数据。6 j- E5 D4 w, D; X3 k% f
4 f F# J! Z: l8 r0 c<FONT color=#ff0000>文件检测函数</FONT>9 p7 I C# w2 Q
: u, E4 K/ n/ [; |- W( a) Q# k7 nC语言中常用的文件检测函数有以下几个。
; w Q2 y0 L% {) `8 [一、文件结束检测函数feof函数调用格式: feof(文件指针); 8 s0 m3 Y N5 U+ S1 z/ I+ J1 R0 r
功能:判断文件是否处于文件结束位置,如文件结束,则返回值为1,否则为0。
8 y. l, [/ @! }# z
1 T2 f" M( P' c; U# Q+ g' ~二、读写文件出错检测函数ferror函数调用格式: ferror(文件指针);
# X" ]- p- Q' o: Z* f( B功能:检查文件在用各种输入输出函数进行读写时是否出错。 如ferror返回值为0表示未出错,否则表示有错。1 d# e# G' S. {
1 k7 b/ n8 n4 a% ~1 i
三、文件出错标志和文件结束标志置0函数clearerr函数调用格式: clearerr(文件指针); - R% |: y/ ?# o' P
功能:本函数用于清除出错标志和文件结束标志,使它们为0值。
H, T4 M4 V- j" T; N1 R1 ~
$ H" u6 H! z2 D/ U<FONT color=#ff0000>C库文件</FONT>
# ], [' F$ B: |4 Z7 y5 Y
P: ?& d, @1 A- y2 `C系统提供了丰富的系统文件,称为库文件,C的库文件分为两类,一类是扩展名为".h"的文件,称为头文件, 在前面的包含命令中我们已多次使用过。在".h"文件中包含了常量定义、 类型定义、宏定义、函数原型以及各种编译选择设置等信息。另一类是函数库,包括了各种函数的目标代码,供用户在程序中调用。 通常在程序中调用一个库函数时,要在调用之前包含该函数原型所在的".h" 文件。
* s6 J$ Z; L9 D! c在附录中给出了全部库函数。 v4 l, V1 C9 R) t* [! P$ b7 a: K
ALLOC.H 说明内存管理函数(分配、释放等)。
- N9 n9 |; @% _; W6 cASSERT.H 定义 assert调试宏。
" P f3 l. x5 |* P8 TBIOS.H 说明调用IBM—PC ROM BIOS子程序的各个函数。
+ [' f( k! D0 N! } a4 ?6 D" VCONIO.H 说明调用DOS控制台I/O子程序的各个函数。& }- ?+ U( X4 \, w- k r7 U
CTYPE.H 包含有关字符分类及转换的名类信息(如 isalpha和toascii等)。
7 B0 X8 y* O, f' i! U4 oDIR.H 包含有关目录和路径的结构、宏定义和函数。7 f# _3 m4 N r1 a9 h6 K
DOS.H 定义和说明MSDOS和8086调用的一些常量和函数。
$ ?! ~8 a. N7 R. MERRON.H 定义错误代码的助记符。9 g9 v% ~, u, Z+ P' ^/ R& q! F p: N
FCNTL.H 定义在与open库子程序连接时的符号常量。% d0 c: E& l( m5 | B
FLOAT.H 包含有关浮点运算的一些参数和函数。
" u K2 L) L' l7 i$ C2 TGRAPHICS.H 说明有关图形功能的各个函数,图形错误代码的常量定义,正对不同驱动程序的各种颜色值,及函数用到的一些特殊结构。
1 \4 V% z+ J% ? ]" AIO.H 包含低级I/O子程序的结构和说明。
" @4 R6 A8 M9 J9 ZLIMIT.H 包含各环境参数、编译时间限制、数的范围等信息。
/ s8 |) V y7 \) G2 B9 B' hMATH.H 说明数学运算函数,还定了 HUGE VAL 宏, 说明了matherr和matherr子程序用到的特殊结构。
% k$ `+ ^# x. \! t% }# c' AMEM.H 说明一些内存操作函数(其中大多数也在STRING.H 中说明)。
- d' v" F4 ]: V; u" v9 W0 QPROCESS.H 说明进程管理的各个函数,spawn…和EXEC …函数的结构说明。
7 O& i/ X) t' Y; c) D$ YSETJMP.H 定义longjmp和setjmp函数用到的jmp buf类型, 说明这两个函数。2 L0 _2 W9 L/ g% C4 w
SHARE.H 定义文件共享函数的参数。1 T* Z# t1 k( i/ f8 V. Y. W
SIGNAL.H 定义SIG[ZZ(Z] [ZZ)]IGN和SIG[ZZ(Z] [ZZ)]DFL常量,说明rajse和signal两个函数。
& _1 W( H5 U# i; ySTDARG.H 定义读函数参数表的宏。(如vprintf,vscarf函数)。
) h# B6 m8 }% LSTDDEF.H 定义一些公共数据类型和宏。
- m6 M1 b9 i- G0 o9 V }, @STDIO.H 定义Kernighan和Ritchie在Unix System V 中定义的标准和扩展的类型和宏。还定义标准I/O 预定义流:stdin,stdout和stderr,说明 I/O流子程序。$ j, U! E4 l. n7 a
STDLIB.H 说明一些常用的子程序:转换子程序、搜索/ 排序子程序等。+ V5 k' p; D7 A2 [
STRING.H 说明一些串操作和内存操作函数。
[9 n7 y2 c# ~7 HSYS\STAT.H 定义在打开和创建文件时用到的一些符号常量。
; O3 a# m q. u) _: t6 e2 qSYS\TYPES.H 说明ftime函数和timeb结构。/ v# ]$ k0 f5 ~/ A% g
SYS\TIME.H 定义时间的类型time[ZZ(Z] [ZZ)]t。4 s3 F+ W- s7 F
TIME.H 定义时间转换子程序asctime、localtime和gmtime的结构,ctime、 difftime、 gmtime、 localtime和stime用到的类型,并提供这些函数的原型。
' l$ p* t3 f6 G3 ZVALUE.H 定义一些重要常量, 包括依赖于机器硬件的和为与Unix System V相兼容而说明的一些常量,包括浮点和双精度值的范围。
6 }( ?5 t2 W# B' M% I5 T' J1 r9 w" i% R) w0 ^) e2 y
<FONT color=#cc0000><B>本章小结</B></FONT>0 G, P* @5 d7 x
& E- J. S7 d8 H$ A. L' v$ ?. r+ j1. C系统把文件当作一个“流”,按字节进行处理。
9 N" X& w$ O" {; u; A7 ], L3 h& R
2. C文件按编码方式分为二进制文件和ASCII文件。9 X: `: w0 \: F5 n8 `% T! Y
# n7 z4 j4 M* `5 Y, R
3. C语言中,用文件指针标识文件,当一个文件被 打开时, 可取得该文件指针。
9 ~* d! ~; C3 e1 O3 y4 A
$ K8 g5 x o; R4. 文件在读写之前必须打开,读写结束必须关闭。9 d% g2 N4 i1 a. N- O
/ _8 W/ Y1 `" L! O. w
5. 文件可按只读、只写、读写、追加四种操作方式打开,同时还必须指定文件的类型是二进制文件还是文本文件。4 A: r7 y, i) O! h5 s
" T; u {, ]# ]* @0 g
6. 文件可按字节,字符串,数据块为单位读写,文件也可按指定的格式进行读写。
: o* q& l' F% p( q, E. H* y
6 Y% @9 A9 h3 d0 ]. G$ k7. 文件内部的位置指针可指示当前的读写位置,移动该指针可以对文件实现随机读写。</P> |
|