在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备被看成文件,因此对文件操作就等同于对磁盘上普通文件的操作。
对文件的操作:打开、读、写、执行、关闭。
文件流
stdin、stdout、stderr是由系统打开的,可直接使用。
FILE *fopen(char *filename,char *mode)
fopen()函数的返回值:fopen函数会获取文件信息(文件名、文件状态、当前读写位置),并将这些信息保存到一个FILE类型的结构体变量中,然后将该变量的地址返回。
FILE *fp=fopen(“D:\demo.txt”,“rb+”);
注:在打开文件时一定要判断文件是否打开成功。
等于运算符的优先级高于赋值运算符。
FILE *fp;
if (fp=fopen("D:\\demo.txt","rb+")==NULL)
{
printf("fail to open\n");
exit(0);
}
fopen函数的打开方式:
文件的读、写、更新
文件分为文本文件和二进制文,默认是文本文件。
关闭文件:
int fclose(FILE *fp) {
fclose(fp);
}
文件打开和关闭的实例:
#include
#include
#define N 100
int main() {
File *fp;
if ((fp=fopen("D:\\demo.txt","rb+"))==NULL)
{
printf("fail to open\n");
}
exit(0);
fclose(fp);
return 0;
}
文本本质上也是二进制
fgetc
fputc
(fgetc和fputc函数每次只能读写一个字符,速度较慢。)
fgets
fputs
(fgets和fputs函数每次只能读写一行)
fread
fwrite
(fread和fwrite函数一次可以读取多行数据)
注:对于windows使用fread和fwrite函数应以二进制的形式打开文件。
fread函数:
fread函数用来从指定文件中读取块数据。所谓块数据就是若干个字节的数据。
注:typedef定义的数据类型后面需要加_t
size_t fread(void *ptr,size_t size,size_t count,FILE *fp)
size_t是在stdio.h和stdlib.h头文件中使用typedef定义的数据类型表示无符号整数(内存区块的指针、每个数据块的字节数、数据的数量、文件指针)
#include
#include
#pragma warning(disable:4996)
#define N 100
int main() {
int a[N], b[N];
FILE *fp;
int size = sizeof(int);
if ((fp=fopen("D:\\del","rb+"))==NULL)
{
printf("fail to open");
exit(0);
}
for (int i = 0; i < N; i++)
{
scanf_s("%d",&a[i]);
}
fwrite(a,size,N,fp);
rewind(fp);
fread(b , size, N, fp);
for (int i = 0; i < N; i++)
{
printf("%d\n",b[i]);
}
fclose(fp);
return 0;
}
如何调试程序?怎样加深记忆、提高效率?
#include
#include
#pragma warning(disable:4996)
#define N 2
struct stu
{
char name[10];
int num;
int age;
float score;
}ba[N],bb[N], *pa, *pb;
int main() {
FILE *fp;
pa=ba;
pb=bb;
if ((fp=fopen("d:\\del.txt","wb+"))==NULL)
{
printf("fail to open\n");
exit(0);
}
for (int i = 0; i < N; i++,pa++)
{
scanf_s("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
}
fwrite(ba,sizeof(struct stu),N,fp);
rewind(fp);
fread(bb, sizeof(struct stu), N, fp);
for (int i = 0; i < N; i++,pb++)
{
printf("%s %d %d %f",pb->name,pb->num,pb->age,pb->score);
}
fclose(fp);
return 0;
}
辨析:
fscanf和fprint函数都是格式化读写函数,他们的读写对象是磁盘文件。
scanf和printf是格式化读写函数,他们的写对象是键盘,读对象是显示器。
随机读写:读写可以从文件的任意位置,需要先移动文件内部的指针,再进行读写。
文件的定位:实现随机读写的关键是要按照要求移动位置指针。
rewind函数:将位置指针移动到文件开头,其原型为
void rewind(FILE *fp)
fseek函数用来将位置指针移动到任意位置,其原型为
int fseek (FILE *fp,long offset,int origin)
SEEK_SET
SEEK_CUR
SEEK_END
注:fseek函数一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错。
文件的随机读写:先移动位置指针,再进行函数读写。
下面代码执行不对???
#include
#include
#pragma warning (disable:4996)
#define N 3
struct stu
{
char name[10];
int num;
int age;
float score;
}boy[N],boys,*pb;
int main() {
FILE *fp;
int m = sizeof(struct stu);
pb=boy;
if ((fp = fopen("d:\\de.txt", "wb+")) == NULL)
{
printf("FAIL TO OPEN\n");
exit(1);
}
printf("input data\n");
for (int i = 0; i < N; i++,pb++)
{
scanf("%s %d %d %f",pb->name,&pb->num,&pb->age,&pb->score);
}
fwrite(boy,m,N,fp);
fseek(fp,m,SEEK_SET);
fread(boy,m,1,fp);
printf("%s %d %d %f",boys.name,boys.num,boys.age,boys.score);
fclose(fp);
return 0;
12.9 C语言实现文件复制功能
文件复制的思路:首先开辟一个缓冲区,再将原文件的数据读到缓冲区,每读完一次就将缓冲区中的内容写入到新建的文件,直到原文件读完。
需要解决的两个关键问题:
(1)开辟多大的缓冲区?避免跨扇区读取.
(2)缓冲区中的数据没有结束标志,如果缓冲区填充不满,如何确定写入的字节数?
回忆一下fread函数的原型
size_t fread(void *ptr,size_t size,size_t count,FILE *fp)
注:fopen一定要以二进制的形式打开文件,不能以文本形式打开。
文件指针:指针变量指向的是文件
注:fopen函数的打开方式
读文件的时候文件必须存在
wb+的作用很大
控制缓冲区:缓冲区和输入流
缓冲区的基地址是不变的,利用指针指向下一个要被读取字符的地址,缓冲区没有数据的时候要将指针刷新,将指针重新指向基地址,。
注:换行符也当成一个字节处理。
缓冲区的刷新就是将指针ptr变为缓冲区的基地址,同时cnt的值为0,因为刷新之后缓冲区没有数据???
可以这样理解么,如果输入流还有数据,那么缓冲区的刷新就是指针指向的刷新,cnt取决于实际情况,如果输入流没有数据了,缓冲区的刷新就是指针指向的刷新,cnt此时的实际情况就是0
自己编写函数即可
顺序文件
散列文件
索引文件
如何插入数据:杯子思想
如何删除数据:临时文件
在实际开发过程中,我们往往会保持新旧数据长度一致,进而减少编程的工作量。
注:实际编程中应该多学、多问、多思考,效率优先,不能死磕不会的,遇到困难需要及时寻求团队的能力并调度资源,进而高效、高质量的完成任务。
文件复制函数、文件删除函数、文件插入函数类比学习???
文件复制函数:
fread函数的定义:
size_t fread(void *ptr,size_t size,size_t count,FILE *fp)
文件复制函数:
/*
文件复制函数(这就是编程中的需求开发,有人想要实现文件复制,开发人员根据需求写出来这个功能)
fSource 要复制的原文件
offsetSource相对原文件的位置偏移
len要复制的内容长度,如果len小于0则需要复制offsetSource后边的所有内容
fTarget要复制到的目标文件
offsetTarget相对目标文件的位置偏移
return成功复制的字节数
*/
long fcopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget) {
int bufferLen = 1024 * 4;
char *buffer = (char *)malloc(bufferLen);
int readCount;
long nBytes = 0;
int n = 0;
fseek(fSource,offsetSource,SEEK_SET);
fseek(fTarget,offsetTarget,SEEK_SET);
if (len<0)
{
while ((readCount=fread(buffer,1,bufferLen,fSource))>0)
{
nBytes += readCount;
fwrite(buffer,readCount,1,fTarget);
}
}
else
{
n = (int)ceil((double)((double)len / bufferLen));//需要完整进入缓冲区的次数
for (int i = 1; i <= n; i++)
{
if (len-nBytes<bufferLen)//一次不完整的占据缓冲区
{
bufferLen = len - nBytes;//
}
readCount=fread(buffer,1,bufferLen,fSource);//fread读取的字节数为n次完整的缓冲区和一次可能的不完整缓冲区
fwrite(buffer,readCount,1,fTarget);
nBytes += readCount;
}
}
fflush(fTarget);
free(buffer);
return nBytes;
}
注:p判断语句要全面考虑
文件内容插入函数:
/*
fp 文件参数,要插入内容的文件
offset 相对文件开头的偏移量
buffer 将插入内容放到的缓冲区大小
len 要插入的内容长度
fileTemp 文件复制时创建的临时文件
len 返回超过插入的字节数
*/
#include
#include
long finsert(FILE *fp, int offset, void *buffer, int len) {
long fileSize = fsize(fp)
File *fileTemp;
if (offset>fileSize||offset<0||len<0)
{
return -1;
}
if (offset==fileSize)
{
fseek(fp,offset,SEEK_SET);
if (!fwrite(buffer,len,1,fp))
{
return -1;
}
}
if (offset<fileSize)
{
fpTemp=tmpfile();//tmpfile()创建临时文件之后会被删除
fcopy(fp, 0, offset, fpTemp, 0);//将前offset个字符保存下来,写入临时文件
fwrite(buffer, len, 1, fpTemp);//在临时文件写len个字节
fcopy(fp,offset,-1,fpTemp,offset+len);//将原文件offset之后的字节放到临时文件offset+len个字节长度内
freopen(FILENAME,"wb+",fp);//freopne函数
fcopy(fpTemp,0,-1,fp,0);
fclose(fpTemp);
}
return 0;
}
注:tmpfile函数用来创建一个临时的二进制文件,可以读取和写入数据,相当于fopen函数以wb+方式打开文件,该临时文件不会和当前存在的文件重名,并且会在调用fclose后或者程序结束后自动删除。
文件内容删除函数:
#include
long fdelete(FILE *fp, long offset, int len) {
long fileSize = getFileSize(fp);
FILE *fpTemp;
if (offset>fileSize||offset<0||len<0)
{
return -1;
}
fpTemp = tmpfile();
fcopy(fp,0,offset,fpTemp,0);
fcopy(fp, offset + len, -1, fpTemp, offset)
freopen(FILENAME,"wb+",fp);
fcopy(fpTemp,0,-1,fp,0);
fclose(fpTemp);
return 0;
}