C语言09- 文件

2018-01-08  本文已影响0人  sanpintian

19:文件

文件:是一组相关数据的有序结合

19.1:文件概念

文件分类:

  1. 普通文件:存储在磁盘等外部设备中,在需要的时候由程序将数据读入内存
  2. 设备文件:外部设备对于操作系统来说也是当做一个文件进行管理,把它们的输入、输出等同于对磁盘文件的读和写。比如显示器、打印机、键盘等。
  3. 文件夹:包含普通文件和子文件夹

文件系统:操作系统中用于组织和管理磁盘上文件的方法以及数据结构。

文件系统作用:为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。程序中文件操作,之后都由文件系统去替程序完成。

文件系统有:

  1. Windows: FAT,NTFS
  2. Linux:Ext2,Ext3,Ext4
  3. Solaris:ZFS(Linux一直想移植,但由于协议冲突等等一直没有实现)
  4. 其他等
VFS.png

如上图文件系统可以理解为三层:

  1. 最上层文件系统的系统调用
  2. 虚拟文件系统 VFS(Virtual Filesystem Switch)
  3. 挂载到VFS中的各实际文件系统

ZFS是一款128bit文件系统,总容量是现有64bit文件系统的1.84x1019倍,其支持的单个存储卷容量达到16EiB(264byte,即 16x1024x1024TB);一个zpool存储池可以拥有264个卷,总容量最大256ZiB(278byte);整个系统又可以拥有2^64个存储 池。可以说在相当长的未来时间内,ZFS几乎不太可能出现存储空间不足的问题。另外,它还拥有自优化,自动校验数据完整性,存储池/卷系统易管理等诸多优点。较ext3系统有较大运行速率,提高大约30%-40%。


文本文件与二进制文件:

  1. 文本文件:基于字符编码,以固定长度的二进制序列进行编码和解码(常见的有ASCII文编码和Unicode编码);(使用notepad就可以读取);(纯文本文件:.c文件.txt.html等)
  2. 二进制文件:基于值编码的文件,二进制文件编码是变长的,具体的长度由具体的格式决定,比如EXE或者BMP二进制文件;(需要专门的工具读取,比如图片就需要专门的读图软件才能打开,如果用notepad打开,就会看见不少乱码);(二进制文件:exe\dll\jpg\png\avi\doc\pdf等)
文本“5678”的存储形式为:字符的ASCII码: 00110101 00110110 00110111 00111000 (四个字节)
值5678的存储形式为:值的二进制: 00010110 00101110 (两个字节)

19.2:文件创建打开与读写

19.2.1:打开文件fopen_s

//文件读写:fopen(), fopen_s()

函数原型:FILE * fopen(const char * path, const char * mode);
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回 NULL,并把错误代码存在errno中。
参数说明:
参数 path字符串包含欲打开的文件路径及文件名,参数 mode 字符串则代表着流形态。

一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在 fopen() 后作错误判断及处理。

//fopen("newfile.txt", "rw, ccs=UTF-8");//默认为ANSI
//fopen是C标准IO库的函数,与非C标准库函数open()函数相比,用fopen打开的文件读写是带缓存的,即用fwrite向文件里写一个字节,一般来讲它不会立刻调用write将该操作提交给kernel,而是积累到一定程度再一起写。

//fopen_s()函数是用于fopen()的新的安全版本。现在都推荐使用fopen_s()来打开文件。它的调用方法:

FILE *pfile=NULL;
errno_t err = fopen_s(&pfile,FILENAME,"wb+");

if(err!=0 || pfile==NULL) {
        return -1;
}

fclose(pfile);完成文件IO后,不要忘调用fclose()函数将文件关闭。
fopen()函数.png

19.2.2:读写文件

在C语言里,可以使用fread/fwrite,fscanf/fprintf,fgets/fputs,fgetc/fputc等函数来进行文件的读写。

C语言中文件读写几种类型:

  1. 二进制文件读写
  2. 格式化输入输出到文本文件
  3. 字符输入输出到文本文件
  4. 字符串输入输出到文本文件
//二进制文件读写
int binary_io() {
     //打开或者创建文件
     char *path = "h:\\doc\\new.txt";
    
     FILE *fp1 = NULL;
     errno_t err;

     err = fopen_s(&fp1,path,"w");

     if(fp1==NULL || err != 0) {
         printf("Open file failed\n");
         return -1;
     }

     //基于fp1这个指针,对文件进行读写等操作
     char buff[]="hello world";
     fwrite(buff,sizeof(buff),1,fp1);

     int data = 0x10;
     fwrite(&data,sizeof(data),1,fp1);

     fclose(fp1);

     //读数据
     err = fopen_s(&fp1,path,"r");
     
     if(fp1==NULL || err!=0) {
         return -1;
     }

     data = 0;
     memset(buff,0,sizeof(buff));

     fread(buff,sizeof(buff),1,fp1);
     printf("buff:%s\n", buff);

     fread(&data,sizeof(data),1,fp1);
     printf("data:0x%x\n", data);
     fclose(fp1);

     return 0;
}

//格式化输入输出到文本文件(将除了ascci之外的数据以文本的方式写入文件)
int format_io() {
     char *file="h:\\doc\\1.txt";
     FILE *pfile=NULL;

     errno_t err = fopen_s(&pfile,file,"w");

     if(err!=0 || pfile==NULL){
         printf("Open file failed\n");
         return -1;
     }
 
     fprintf(pfile,"%s %x %lf","hello-world", 0x10,3.1415);
     fclose(pfile);

     char buff[64]={0};
     int data=0;
     double d = 0.0;
 
     err=fopen_s(&pfile,file,"r");

     if(err!=0) {
         printf("Open file failed\n");
         return -1;
     }
     
     fscanf_s(pfile,"%s%x%lf",buff,64,&data,&d);
     printf("buff:%s,data:%d,d:%lf\n",buff,data,d);

     fclose(pfile);

     return 0;
}

//字符输入输出到文本文件
 char *str="hello world, goodbye world!";
 while(*str!='\0') {
         fputc(*str,pfile);
         str++;
}

//字符串输入输出到文本文件


19.3:文件其他相关操作

  1. fseek(被打开文件的指针, 移动的偏移-偏移值可以为正或者为负,代表第二个参数的偏移相对于什么位置):移动文件的当前读写位置指针。fseek(fp,50,SEEK_CUR);将文件的读写指针移动到当前位置后面50个字节。而SEEK_SET则是相对于文件开头,SEEK_END是文件结尾。fseek(fp, -50, SEEK_END);就是将文件读写指针移动到距离文件结束50个字节的位置。
  2. rewind():rewind(fp)执行函数后,会将文件fp当前的读写位置移动到文件的开头位置。等同于:fseek(fp,0,0);
  3. feof():用来判断文件当前位置是否达到文件末尾,如果到达,就返回真,否则为假。
  4. rename/remove/mkdir:文件的重命名,删除,创建文件夹都是与文件有关的一些基本操作。
  5. ftell():用于获取文件读写指针的当前位置。可以结合fseek()函数来计算文件的大小。

19.4:结构体IO与优化

//定义结构体:

#define MAXLEN 64

typedef struct _record {
    char name[MAXLEN];
    int age;
}record,*precord;

record  r =  {“tom”,25};

//按下方法将r写入文件,导致r成员name[MAXLEN]中大量零被写入文件中存放很多无用数据零。这样直接写入不是最佳方法:
fwrite(&r,sizeof(r),1,fp);

//考虑将结构体r按格式写入文件:
//假如name[MAXLEN]中的有效字符个数为len,那么写入方法为:
Len+name+age

//也就是先将name的字符数len写入文件,接着写入name的有效字符数据,再写入年龄数据。其中len和age各为4个字节的整数,name为字符。比如对于下面结构体记录数据:
record  r1  =  {“tom”,25};
record  r2  =  {“lily”,22};

//3”tom”25 4”lily”22;这样在每个name前面都用一个长度来记录name中字符的个数,就可以避免将过多的无用的零写入文件了。如下

int opt_write_file(FILE *fp) {
    char ch;
    
    do {
        record rcd={0};
        printf("Please input name and age\n");
            
        scanf_s("%s%d", rcd.name,MAXLEN,&rcd.age);
            
        size_t len = strlen(rcd.name);  
        fwrite(&len,sizeof(len),1,fp);//先写入name中字符的个数
        fwrite(rcd.name,len,1,fp);//接着写入name字符数
        fwrite(&rcd.age,sizeof(int),1,fp);//再写入年龄
    
        printf("input q to quit,else to continue\n");       
        //scanf_s("%c",&ch,1);
        //fflush(stdin);
            
        ch = _getch();
        if(ch=='q')
            
        break;
    } while (1);
    
    return 0;

}

int opt_read_file(FILE *fp) {
    while(!feof(fp)) {
        record rcd = {0};
        size_t len=0;
        int res = fread(&len,sizeof(len),1,fp);//先读取name的字符数
        if(res == 0) {
                return 0;
        }
            
        fread(rcd.name,len,1,fp);//再按照字符数读取name
        fread(&rcd.age,sizeof(rcd.age),1,fp);//再读取年龄
        printf("name:%s,age:%d,res:%d\n", rcd.name,rcd.age,res); 
    }

    return 0;
}

上一篇下一篇

猜你喜欢

热点阅读