两种I/O文件函数
- fopen ANSI 标准文件I/O,基于低层次I/O
- open 低层次I/O
ANSI I/O本质
- DOS/WINDOWS平台,MS DOS 文件读写, 汇编语言编写
- LINUX平台,unix/linux文件读写,C语言编写
文件类型FILE
包含一个指针
包含一个stream (
C语言把文件看成stream )
file open
r Open text file for reading
r+ Open for reading and writing
w Truncate file to zero length or create text file for writing
w+ The file is created if it does not exist, otherwise it is truncated
a Open for appending (writing at end of file).
The stream is positioned at the end of the file.
fopen三种基本模式r,w,a只允许针对文本文
C语言允许最大同时打开16个文件
实际只允许同时打开13个文件,因为有另外三个标准文件stdout,stdin,stderr
文件读写的基本语句
1
| #include <stdio.h>
FILE
|
2
|
if((stream=fopen("testtmp","r+"))==NULL)
{ printf("open file error/n");
return;
}
|
3
| int ret;
if ((ret=fclose(stream))==-1) printf("close file error/n");
|
顺序读文件(按字符)
while((c=fgetc(stream))
!= EOF ) printf("%c",c); 每读一个字符,
文件指针会(自动)移动一格
顺序读文件(按字符串)-------从一个文件依次读一行,直到读到NULL
char record[100],*re;
while ((re=fgets(record,
100 ,stream))!=
NULL )
printf("%s",record);
读一行,以回车换行作为读一行的尾部标志
从文件中fgets的字符串是读一行,即包括了回车
所以printf("%s")即可,不用"%s/n",因为字符串里面带了回车了
这也是个漏洞,字符串里带了回车
所以非常不利于字符串比对strcmp
scanf读文件,文件指针不移动,需要手动fseek移指针
fseek(stream,3,SEEK_SET);
fscanf(stream,"%c",&c[0]);
fseek(stream,11,SEEK_SET);
fscanf(stream,"%c",&c[1]);
|
文件读写到文件末尾
文件指针会指到NULL (而不是EOF,EOF不是地址,而是结尾字符
- fput(),fgetc()会返回EOF
- fgets()会返回NULL
linux和DOS对文件的结束判别是不同的
linux没有文件结束符 ,是以目录项的文件长度做为文件结束的判别手段
DOS看0x1a文件结束符
linux 和DOS的换行符的区别
linux
| DOS
|
0x0a 即 10
即’/n’
即LF
| CR,LF双码
|
读文件时, 不能用unsigned char c作为返回值, 因为EOF不在unsigned范围内
char c;
fscanf读文本文件里面的数字,既可以直接用%c,也可以用%d
用%c(char)读,可以读出3,1
| 用%d(int)读,也可以读出3,1
|
fseek(stream,3,SEEK_SET);
fscanf(stream,"%c",&c[0]);
fseek(stream,11,SEEK_SET);
fscanf(stream,"%c",&c[1]);
| fseek(stream,ii,SEEK_SET);
fscanf(stream,"%d",&ln[i].frame);
fseek(stream,ii+8,SEEK_SET);
fscanf(stream,"%d",&ln[i].line);
|
只不过这两个3,1,一个是实际值3,1,一个是'3','1'
几个常见特殊字符的整型数字
char
| int
|
空格' '
| 32
|
TAB键
| 9
|
回车
| 10
|
文件结束EOF
| -1
|
字符串结束符号
| 数字0(不是字符‘0’)
|
TAB字符处理要小心,经过到记事本copy/paste后,TAB键被转化成几个空格
for(;str[i]==' '||str[i]==' ';i++);
|
但经过到记事本copy/paste后,TAB键被转化成几个空格
所以系统总报warning:
tmp.c:58: warning: comparison is always false due to limited range of data type
tmp.c:59:27: warning: character constant too long for its type
|
fgets字符串指针改成字符串数组,消灭了Segmentation fault错误
char *re,*rec;
re=fgets(rec,100,srcstream);
出Segmentation fault错误
|
改成
char *re,rec[100];
re=fgets(rec,100,srcstream);
错误消失
|
一个普通的显示文件内容 的函数
printfile(FILE *stream,char *filename,int or)
{
int i,re;
char rec[100];
FILE *tmpstream;
if (or)
{
if((tmpstream=fopen(filename,"r"))==NULL)
{ printf("open file error/n");
return 0;
}
for(i=0;(re=fgets(rec,100,tmpstream))!=NULL;i++) printf("[%d]:%s",i,rec);
显示文件内容时,每行加行号
if ((re=fclose(tmpstream))==-1) printf("close file error/n");
} else
{
fseek(stream,0,SEEK_SET);
for(i=0;(re=fgets(rec,100,stream))!=NULL;i++) printf("[%d]:%s",i,rec);
}
}
|
printfile(NULL,"testtmp.tmp",1 );
[0]:total 48
[1]:1 macg macg 3301 Jan 16 02:16 file.c
[2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile
[3]:1 macg macg 0 Jan 16 02:56 testtmp
[4]:this is a testthis is a testthis is a testthis is a testthis is a testth
[5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o
|
if((stream=fopen("testtmp","r+"))==NULL)
{ printf("open file error/n");
return 0;
}
printfile(stream ,NULL,0 );
[0]:total 48
[1]:-rw-rw-r-- 1 macg macg 3301 Jan 16 02:16 file.c
[2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile
[3]: -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp
[4]:this is a testthis is a testthis is a testthis is a testthis is a testth
[5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o
|
按SED/AWK原理,读记录/行的函数——是从文件中取第recno行
int getrecord(char *rec,FILE *stream,int recno)
{
int i,ret;
char *re;
for(i=0;i<=recno;i++) re=fgets(rec,100,stream); 数行,并一边数一边读行
if(re==NULL) return 0; 如果所取行超过文件长度,则返回0
else return 1;
}
|
getrecord(record,stream,4); 取记录/行,取文件第四行
printf("record is %s/n",record);
printf("/n/n/n");
|
$ ./tmp
record is -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp
|
按SED/AWK原理,读字段的函数——从字符串中取第valueno字段
int getvalue(char *str,int valueno,char *value)
{
int i,j,vali;
数字段
for(i=0,vali=0;vali<valueno;vali++)
{
for(;str[i]==' '||str[i]==9;i++);
for(;str[i]!=' '&&str[i]!=9&&str[i]!=0;i++);
if(str[i]==0) return 0;
}
取字段
for(;str[i]==' '||str[i]==9;i++);
for(j=0;str[i]!=' '&&str[i]!=9&&str[i]!=0;i++,j++)
{
value[j]=str[i];
}
value[j]=0;
}
|
for(i=0;i<20;i++)
{
if(getvalue(record,i,val)) printf("no %d is %s/n",i,val);
}
|
$ ./tmp
record is -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp 事先故意编辑过此行,加了几个TAB,连行尾也含TAB
no 0 is -rw-rw-r--
no 1 is 1
no 2 is macg
no 3 is macg
no 4 is 0
no 5 is Jan
no 6 is 16
no 7 is 02:56
no 8 is testtmp
no 9 is
|
fputs(stren,streamwrite) 写入的串不带回车,fprintf(strea,"%s",str)写入的串也不带回车,所以,要写入文件回车,必须写入/n
fputs(stren,streamwrite);
cat aaa.txt
008421aa
|
fprintf(streamwrite,"%s/n ",stren);
cat aaa.txt
008421
aa
|
文件很难进行“修改写”,建议文件追加
最好的办法就是写到新文件 ,或写到stdout再重定向入文件。然后把新文件覆盖旧文件
writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi)-------修改文件的函数
实际是从 srcfile读出文件
把其中符合frs[j].recno的行,修改成frs[j].record
其他行不变,仍旧用srcfile读出的行
然后把这些行重新写入一个新文件tmpfile
writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi
o tmpfilename 新文件名
o srcfilename 旧文件名
o struct Filerecord *frs 放置要修改的行的结构体数组
struct Filerecord{
char record[100];
int recno;
};
struct Filerecord fr[20];
o frsi 结构体数组的元素数量
|
writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi)
{
int no,ret,i,j,bol;
char *re,rec[100];
FILE *tmpstream,*srcstream;
if((tmpstream=fopen(tmpfilename,"w+ "))==NULL) 写入一个新
{ printf("open file error/n");
return 0;
}
if((srcstream=fopen(srcfilename,"r"))==NULL)
{ printf("open file error/n");
return 0;
}
for(no=0;(re=fgets(rec,100,srcstream))!=NULL;no++)
{
for(j=0,bol=1;j<frsi&&bol;j++)
{
if(no==frs[j].recno) {
bol=0;
strcpy(rec,frs[j].record);
}
}
fputs(rec,tmpstream);
}
if ((ret=fclose(tmpstream))==-1) printf("close file error/n");
if ((ret=fclose(srcstream))==-1) printf("close file error/n");
}
|
例子:去掉文件中行的TAB键和起首空格或TAB
执行:
读出原文件每行,checkinvaild() ,看是否含有TAB,起首空格/TAB
如果确实有,就对此行处理,重新组合,去掉TAB,起首空格/TAB,然后写入struct person fr[20]数组
writefile("testtmp.tmp","testtmp",fr,fri);
------------------the sick file----------------------------------------
[0]:total 48
[1]:-rw-rw-r-- 1 macg macg 3301 Jan 16 02:16 file.c
[2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile
[3]: -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp
[4]:this is a testthis is a testthis is a testthis is a testthis is a testth
[5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o
--------------------modify line--------------------------------------
fri is 2,the new line is following:
[1]row:1 macg macg 3301 Jan 16 02:16 file.c
[3]row:1 macg macg 0 Jan 16 02:56 testtmp
-------------------new file---------------------------------------
[0]:total 48
[1]:1 macg macg 3301 Jan 16 02:16 file.c
[2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile
[3]:1 macg macg 0 Jan 16 02:56 testtmp
[4]:this is a testthis is a testthis is a testthis is a testthis is a testth
[5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o
|
fseek(stream,offset,mode)的三个mode
SEEK_SET:从开头数第几个offset
SEEK_CUR: 从当前数第几个offset
SEEK_END: 从结尾倒数第几个offset
文件指针位置是从0(文件头)开始算的
fseek(stream,0,SEEK_SET);
int ftell(FILE *stream)
返回stream的当前指针位置
简单的取文件大小size的操作
fseek(stream,0,SEEK_END);先将指针指向文件尾部
ret=ftell(stream); 再获得指针所指的当前地址
这个指向文件尾部的指针地址就是文件大小
|
取文件大小的操作对读写文件很有用,因为读写文件很不好判定文件结尾。
文件例子
$ cat tmp.c
#define DEBUG 0
#include <stdio.h>
#include <string.h>
#include <unistd.h>
struct Filerecord{
char record[100];
int recno;
};
main()
{
char directory[100];
#ifdef SYST
getcwd(directory,100);
printf("current directory is %s/n",directory);
#endif
filemanage();
}
filemanage()
{
int i,j,fri,ret;
char record[100],val[50],modi_rec[100];
FILE *stream;
struct Filerecord fr[20];
if((stream=fopen("testtmp","r+"))==NULL)
{ printf("open file error/n");
return 0;
}
printf("------------------the sick file-----------------------/n");
printfile(stream,NULL,0);
for(i=0,fri=0;getrecord(record,stream,i);i++)
{
if (DEBUG) printf("no %d row is:%s/n",i,record);
if(ret=findinvalid(record))
{
for(j=0;getvalue(record,j,val);j++)
{
if (DEBUG) printf("no %d's val is %s %d/n",j,val,val[0]);
if(j==1) strcpy(modi_rec,val);
if(j>1) {
strcat(modi_rec," ");
strcat(modi_rec,val);
}
}
fr[fri].recno=i;
strcpy(fr[fri].record,modi_rec);
if (DEBUG) printf("fr[%d] is %d %s/n",fri,fr[fri].recno,fr[fri].record);
fri++;
}
if (DEBUG) printf("---------------------------------/n");
}
printf("--------------------modify line--------------------/n");
printf("fri is %d,the new line is following: /n",fri);
for(i=0;i<fri;i++)
printf("[%d]row:%s",fr[i].recno,fr[i].record);
if ((ret=fclose(stream))==-1)
{
printf("close file error/n");
return 0;
}
writefile("testtmp.tmp","testtmp",fr,fri);
printf("-------------------new file-----------------/n");
printfile(NULL,"testtmp.tmp",1);
}
int findinvalid(char *str)
{
int i,ret;
i=0;
if(str[i]==' ')
{
if (DEBUG) printf("str[%d] == %d/n",i,str[i]);
return 32;
}
for(i=0;str[i]!=9&&str[i]!=0;i++) ;
if(str[i]==9)
{
if (DEBUG) printf("str[%d] == %d/n",i,str[i]);
return 9;
} else
{
if (DEBUG) printf("str[%d] == %d/n",i,str[i]);
return 0;
}
}
|
$ make
gcc -g -c tmp.c
tmp.c: In function 鈥榩rintfile鈥?
tmp.c:158: warning: assignment makes integer from pointer without a cast
tmp.c:158: warning: comparison between pointer and integer
tmp.c:165: warning: assignment makes integer from pointer without a cast
tmp.c:165: warning: comparison between pointer and integer
gcc -o tmp tmp.o -g
|
$ ./tmp
------------------the sick file----------------------
[0]:total 48
[1]:-rw-rw-r-- 1 macg macg 3301 Jan 16 02:16 file.c
[2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile
[3]: -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp
[4]:this is a testthis is a testthis is a testthis is a testthis is a testth
[5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o
--------------------modify line--------------------
fri is 2,the new line is following:
[1]row:1 macg macg 3301 Jan 16 02:16 file.c
[3]row:1 macg macg 0 Jan 16 02:56 testtmp
-------------------new file-------------------------
[0]:total 48
[1]:1 macg macg 3301 Jan 16 02:16 file.c
[2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile
[3]:1 macg macg 0 Jan 16 02:56 testtmp
[4]:this is a testthis is a testthis is a testthis is a testthis is a testth
[5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o
|