C语言-文件操作

二进制文件与文本文件

内部存储器与外部存储器

什么是文件?

  • 数据一般以文件的形式为用户及应用程序使用

使用文件的好处?

  • 使程序与数据分离
  • 在不同用户间进行数据共享
  • 长期保持数据(相对于内存)

文件的分类

  • 按文件的逻辑结构

     	- 记录文件:具有一定结构的记录组成(定长和不定长)
     
     	- 流式文件(stream):由一个个字符(字节)数据顺序组成
     	- 数据流
     	- 字节流
    

c语言中的文件模型就是流式文件。

  • 通过一个流(和键盘相关)获得全部的输入
  • 通过另一个流(和屏幕相关)获得全部的输出

c语言中的标准流

文件指针标准流默认含义
stdin标准输出键盘
stdout标准输出终端显示器屏幕
stderr标准错误输出终端显示器屏幕
scanf(), getchar(), gets() 通过stdin获得输入
printf(), putchar(), puts() 通过stdout进行输出

输入输出重定向(redirection)

  • 某些操作系统允许标准输入输出重定向文件
  • DOS和UNIX允许程序从文件获得输入或者向文件写数据
    这种重定向,程序本身是感受不到的
  • 输入重定向
  • D:>demo < infile.text
  • 从终端(键盘)输入数据改为从文件中读入数据
  • 输出重定向
  • D:>demo > outfile.text
  • 从终端(显示器)输出数据重定向到文件
  • 按数据的组织形式

  • 文本文件(Text file)

c程序的源代码
用字节表示字符的字符序列,存储每个字符的ASCII码

  • 整数123在文本文件中占3个字节,分别存放这三个字符的ASCII码
    按行划分:必须用特殊的字符标记行的结尾
    某些OS还用特殊的字符标记文件的末尾:如DOS将Ctrl+Z设为文件结束符
  • 二进制文件(Binary file)

可执行的c程序

  • 按照数据在内存中的存储形式(二进制)存储到文件
  • 存储的字节不一定表示字符,无需ASCII码表进行字符变换,读写速度快
  • 不是按行划分的:可合法地包含任何字符,故不可能留出文件结束符
  • 必须按照数据存入的类型格式读出才能恢复本来面貌
  • 按int型读,0x0064,是整数100
  • 按float读,0x00000064,是浮点型1.4012985e-43,近似为0
  • 公开的标准文件格式
  • bmp tif gif jpg mp3
  • 不公开或加密的文件格式
  • Microsoft Word 的doc格式

文件的打开和关闭

文件与流的关系

  • 程序通过文件打开操作将流与设备联系起来,文件打开后,可以在程序和文件之间交换数据
  • 建立文件:由程序在磁盘上建立文件
  • 写入数据:文件打开后,通过写操作将数据存入该文件
  • 读取数据:由程序打开某个已有文件,通过读操作将文件中的数据读入内存供程序使用
  • 程序通过文件关闭操作断开流与文件的联系

文件指针

  • c程序中流的打开和关闭是通过文件指针实现的
  • 文件指针类型为FILE *
FILE * fp;

文件打开后一定要检查是否打开成功

if(fp == NULL) {
	printf("Failure to open!\n");
	exit(0);
}
  • 编译器会将'\'看成转义字符,如\n\t,所以需要改成\\
  • Windows用反斜杠\分割路径,UNIX用斜杠/(不会出现此问题)
文本文件
r只读必须是已存在的文件
w只写无论文件是否存在,都新建一个文件
a追加向文件尾添加数据,该文件必须已经存在
r+读写打开一个已存在的文件,用于读写
w+读写新建一个文件,可读可写
a+读写在文件尾追加数据,可读可写

w:若文件不存在,新建文件;
若文件存在,将原文件内容覆盖
w+:若文件不存在,新建文件;
若文件存在,清空文件;

文件的关闭

int fclose(FILE *fp);
//若成功关闭,返回值为0
//关闭有错,返回为非零值(驱动器无盘或者盘空间不够,文件关闭失败会导致数据丢失、文件破坏,甚至程序出现随即错误
  • 把遗留在缓冲区的数据写入文件,实施操作系统级的关闭
  • 同时,释放与流联系的文件控制块FCB,以便以后重复使用
  • 文件用完一定要关闭。否则可能导致数据丢失,甚至影响其他文件的打开(系统限制同时打开状态的文件总数)

按格式读写文件

读文件

读文件方向:从外存到内存

int a;
char b;
fscanf(fp,"%d %s", &a, &b); 

//函数返回值为读到的参数个数,可以作为条件来判断输入是否结束

写文件

fprintf(fp, "%d %c", a, b);

返回值:写到文件中的字符个数
写错误,返回一个负数

字符和字符串文件的读写

int fputs(int c, FILE *fp);
//向fp指向的文件输出字符c
//写入成功,返回c。写入错误,返回EOF

//从屏幕读入,保存到文件中
int ch;
ch = getchar();
while(ch != '\n'){
	fputc(ch, fp);
	ch = getchar();
}


```c
int fgets(FILE *fp); 
//从文件读入,输出到屏幕上
int ch;
ch = fgetc(fp);		
while(ch != EOF){  //while( !feof(fp) ) 检测到文件尾返回非零,否则返回0值
	putchar(ch);
	ch = fgetc(fp);
}

下面先输出字符,再判断是否到达文件尾的方式,会多输出一个文件结束符

long pos;
while( !feof(fp)){
	pos = ftell(fp);
	ch = fgetc(fp);
	printf("%c %ld\n", ch, pos);
}

开始时文件位置指针指向第一个字符,ftell返回值为0;
fgetc读走一个字符之后,fp自动指向下一个字符;
读完最后一个字符之后,fp指向文件末尾,即指向文件结束符EOF;
直到fgetc把EOF也读走之后,feof才能探测到文件尾。
feof函数总结:
feof在读完文件所有内容之后,再执行一次读文件操作,将文件结束符EOF读走,才能返回真(非0)值。

改进1:先判断是否到达文件尾,后输出字符。不会输出文件结束符

pos = ftell(fp);
c = fgetc(fp);
while(!feof(fp)){
	printf("%c %ld", c, pos);
	pos = ftell(fp);
	c = fgetc(fp);
}

最后一行若接收到文件结束符EOF,feof就会返回非零值,就会跳出循环

改进2:用EOF判断是否到达文件尾

pos = ftell(fp);
c = fgetc(fp);
while(c != EOF){
	printf("%c %ld", c, pos);
	pos = ftell(fp);
	c = fgetc(fp);
}
//简化
pos = ftell(fp);
while((c = fgetc(fp)) != EOF){
	printf("%c %ld", c, pos);
	pos = ftell(fp);
}

用EOF判断存在问题:读到文件尾或者读取错误时,fgetc()都返回EOF,无法区分

用ferror()判断是否读取错误,出错返回非0值

if(ferror(fp){
printf("error on file.\n");}

按行读取文件

puts(),将字符串写入标准输出流,并且在其后添加一个换行符;
fputs(),不会自动添加换行符
gets(char *s),从键盘读一个字符,读到换行符停止
【get会将字符串保存到s指向的缓冲区,然后返回字符串的首地址,但是不考虑缓冲区大小,容易造成缓冲区溢出,给缓冲区溢出攻击造成可乘之机】
fgets(char *s, int n, FILE *fp),从文件读取字符串,最多读n-1个字符。当遇到回车换行符,文件末尾,或者读满n-1个字符时,函数返回字符串的首地址
【参数n限制了字符串的长度,解决了缓冲区溢出问题。用fgets(buf, sizeof(buf),stdin)代替 gets(buf)更安全】

char *gets(char *s);
char *fgets(char *s, int n, FILE *fp);
【相同点】都会在字符串末尾添加’\0’;
读取失败或者读到文件尾都返回NULL;
读取失败用ferror确定,读到文件尾用feof确定。
【不同点】对换行符的处理不同
gets:不保留换行符\n,替换为\0
fgets:保留并存储\n,在换行符后面添加\0

用fputs代替fputc

从键盘输入,输出到屏幕

char str[80];
gets(str);  //fgets(str, sizeof(str), stdin);  规定输入字符串的长度
fputs(str, fp);

从文件读出,输出到屏幕

char str[80];
fgets(str, sizeof(str), fp);
puts(str);

按数据块读写文件

从fp所指文件读数据,并将数据存到a所指向的地址

读文件

num = fread(a, sizeof(char), n ,fp);
//n个块,每个块占一个字节

返回值为实际写入的数据块个数,应该等于n,除非出现错误

写文件

num = fwrite(a, sizeof(char), n ,fp);

随机读写与文件缓存

文件定位

void rewind(FILE fp),使文件位置指针重修指向文件开始位置
long ftell(EILE
fp),返回当前文件位置指针相对于文件起始位置的字节偏移量
int fseek(FILE *fp,long offset ,int fromwhere),改变文件指针位置
offset:指针偏移量(字节数)
fromwhere:起始位置
【SEEK_SET或0】:文件开始
【SEEK_CUR或1】:当前位置
【SEEK_END或2】:文件末尾

文件缓冲

向磁盘输出数据:数据->缓冲区,装满缓冲区后->磁盘文件
从磁盘读入数据:一次性从磁盘文件将一批数据输入到缓冲区,然后再从缓冲区逐个读入数据到内存中某个变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值