二进制文件

  • Post author:
  • Post category:其他


二进制文件

  • 其实所有的文件最终都是二进制的
  • 文本文件无非是用最简单的方式可以读写的文件

    • more,tail
    • cat
    • vi
  • 而二进制文件是需要专门的程序来读写的文件
  • 文本文件的输入输出是格式化,可能经过转码

文本 VS 二进制

  • Unix喜欢用文本文件来做数据存储和程序配置

    • 交互式终端的出现使得人们喜欢用文本和计算机“talk”
    • Unix的shell提供了一些读写文本的小程序
  • Windows喜欢用二进制文件

    • DOS是草根文化,并不继承和熟悉Unix文化
    • PC 刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
  • 文本的优势是方便人类读写,而且跨平台
  • 文本的缺点是程序输入输出要经过格式化,开销大
  • 二进制的缺点是人类读写困难,而且不跨平台

    • int 的大小不一致,大小端的问题。。。
  • 二进制的优点是程序读写快

程序为什么要文件

  • 配置

    • Unix用文本,Windows用注册表、
  • 数据

    • 稍微有点量的数据都放数据库了
  • 媒体

    • 这个只能是二进制的
  • 现实是,程序通过第三方库来读写文件,很少直接读写文件了

二进制读写

size_t fread(void * restrict ptr,size_t size,size_t nitems,FILE* restrict stream);

size_t fwrite(const void* restrict ptr,size_t size,size_t nitems,FILE * restrict stream);
  • 注意FILE指针是最后一个参数
  • 返回的是成功读写的字节数

为什么nitem?

  • 因为二进制文件的读写一般都是通过对一个结构变量的操作来进行的
  • 于是nitem就是用来说明这次读写及几个结构变量!
#student.h

#ifndef _STUDENT_H_
#define _STUDENT_H_

const int STR_LEN = 20;

typedef struct _student{
    char name[STR_LEN];
    int gender;
    int age;
}Student;

#endif



#main.c


#include<stdio.h>
#include "student.h"

void getList(Student aStu[],int number);
int save(Student aStu[],int number);

int main(int argc,char const * argv[]){

    int number = 0;
    printf("输入学生数量:");
    scanf("%d",&number);
    Student aStu[number]; 

    getList(aStu,number);
    if(save(aStu,number)){
        printf("保存成功\n");
    }else{
        printf("保存失败\n");
    }

    return 0;
}


void getList(Student aStu[],int number){

    char format[STR_LEN];
    sprintf(format,"%%%ds",STR_LEN-1);//"%19s"

    int i;
    for(i = 0;i<number;i++){

        printf("第%d个学生:\n",i);
        printf("\t姓名:");
        scanf(format,aStu[i].name);
        printf("\t性别 (0-男 1-女 2-其他):");
        scanf("%d",&aStu[i].gender);
        printf("\t年龄:");
        scanf("%d",&aStu[i].age); 
    }
}

int save(Student aStu[],int number){

    int ret = -1;
    FILE *fp = fopen("student.data","w");
    if(fp){
        ret  = fwrite(aStu,sizeof(Student),number,fp);
        fclose(fp);
    }
    return ret = number;
}



在文件中定位

long ftell(FILE * stream);
int fseek(FILE * stream,long offset,int whence);
 SEEK_SET//从头开始
 SEEK_CUR//从当前位置开始
 SEEK_END//从尾开始(倒过来)
#include<stdio.h>
#include "student.h"

void read(FILE *fp,int index);

int main(int argc,char const *argv[]){

    FILE *fp = fopen("student.data","r");
    if(fp){
        fseek(fp,0L,SEEK_END);
        long size = ftell(fp);
        int number = size/sizeof(Student);
        int index = 0;
        printf("有%d个数据,你要看第几个:",number);
        scanf("%d",&index);
        read(fp,index-1);
        fclose(fp);
    }
    return 0;
}


void read(FILE *fp,int index){
    fseek(fp,index*sizeof(Student),SEEK_SET);
    Student stu;
    if(fread(&stu,sizeof(Student),1,fp)==1){
        printf("第%d个学生:",index+1);
        printf("\t姓名:%s\n",stu.name);
        printf("\t性别:");
        switch(stu.gender){
            case 0:printf("男\n");break;
            case 1:printf("女\n");break;
            case 2:printf("其他\n");break;
        }
        printf("\t年龄:%d\n",stu.age); 
    }
}

可移植性

  • 这样的二进制文件不具有可移植性

    • 在int为32的机器上写成的数据文件无法直接在int为64位的机器上正确读出
  • 解决方案之一是放弃使用int,而是typedef具有明确大小的类型
  • 更好的方案是用文本