C语言复习:(九)文件

文件的优点:

        1.1独立于程序,程序可保存记录在本地磁盘

        1.2文件隶属于磁盘存储,不属于内存;所以存入数据不需要申请内存,可以实现无限存储。(此无限存储在某些时候比链表无限存储更为方便)

对文件操作的完整流程:

打开文件 ---> 操作文件 ---> 关闭文件

1.打开文件

        1.1不同的打开文件的方式,基础类型分别为"r,w,a,b,+"

        "r"为只读,需要原文件存在;

        "w"为重写,不需要原文件存在,有创建文件的功能。

        "a"为添加,不需要原文件存在,重文章末尾添加,有创建文件的功能。

        "b"为二进制格式,可与上诉组合。(二进制在指针移动有优势,如果不是二进制文件,那么存储的字符如果是ASCII表中的字符那么就占两个字节,如果是0~9的字符,那么只占一个字节。由于不知道文件指针接下来要读取的字符是什么类型,不能让文件指针偏移指定字节,而二进制可以)

        "+"可以与上述任意组合,形成附加功能项,其中:

        "r+"为可以读取,也能够写入,读取和写入都是从文件起始位置开始。原文件应当已经存在,原先数据不会因为写入数据而消失。

        "w+"为可以读取,也能够写入,原来的数据全部消失,相当于从新创了一个新文件。在数据录入完成之后就能够从文件起始位置开始读取。

        "a+"为可以读取,也能够写入,需要在文件最后添加完数据之后,开始从文件起始位置开始读取。有创建文件的功能。

        特别注意:“r+”与“a+”的区别是录入的位置不同,读取和写入的顺序不同。

打开文件语句:

在VS2019中,fopen()函数已经被fopen_s()取代;其参数为:文件指针,文件名(文件路径),文件类型;

其中文件名和文件路径都是const char* 类型的(常数字符指针数组类型),所以可以把fopen_s()函数打包在一个自定义函数中:

static FILE* SetNewFile;

void Main_Control_Files_Fun(FILE** File_object,const char* FileName,const char* State)
{
	fopen_s(File_object, FileName, State);
}

int main()
{
	Main_Control_Files_Fun(&SetNewFile,"Flies\\BasicData.txt", "ab+");

	fclose(SetNewFile);
	
	return 0;
}

图1

const char* Name = "Hello World!"为合法赋值;

const char Name[] = "Hello World"也为合法赋值;

同为为定义大小const char类型指针数组,系统为其自动赋予大小。其能代表整个字符串。

2.操作文件:

        2.1逐个字符输入输出:getc(),putc()函数;

        getc()函数为从文件中读取一个字符;

        其调用方式为:

FILE* fp;
char word;
word = getc(fp);

        putc()函数为放置一个字符到文件中,

        调用方式为:

FILE* fp;
char word;
putc(word,fp);

        因为以字符串输入的fputs()函数需要缓冲区,比较麻烦,所以如果往文件中输入字符,putc()函数成为主要调用函数。

以上功能可以用于记录用户从键盘输入的指令,调用功能函数

        fscanf()函数,调用方式:

int a = 0,b = 0;
FILE* fp;
fscanf(fp,"%d%d",&a,&b);

        与getc()不同于:getc()是从文件接收字符,fscanf()是从文件接受数据;

        fprintf()函数,调用方式:

FILE* fp;
int a,b;
fprintf(fp,"\n %d \n %d ",a,b);

        将数据输入到文件中时最好将量数据隔开区分为两个数据。

        与上同理,fprintf()与putc()不同于调用字符和数据。

2.2文件读取结束判定,feof()函数,当文件指针到达文件末尾处时,feof()函数的返回值为0;

        feof() == 1文件未结束

        feof() == 0文件结束

2.3文件指针移动

fseek()函数,用来移动文件指针。

调用形式为:

FILE* fp;

fseek(fp,2L*sizeof(int),SEEK_CUR);

//二进制文件

其中fp为文件指针,第二个参数为移动字节,SEEK_CUR为当前指针位置。与其相应的还有SEEK_SET文件起始位置,SEEK_END文件结束位置。

非二进制文件只能用此函数调用到首位,字节参数为0L。

ftell()函数:获取从文件起始位置到文件指针位置的字节长度。

rewind()函数:回到文件起始位置,无用,于fseek(fp,0L,SEEK_SET);相同。

3.关闭文件

关闭函数fclose();

关闭之后文件指针位置重置。如果是输入操作的话,只有关闭文件之后才会将缓冲区的数据输入到文件当中,如果不关闭文件,会导致数据丢失。

问题:

1.制作游戏界面时,如何将设置数据,玩家数据保存在本地进行调用?

2.制作游戏数据交互系统时,如何存储人物数据?

3.游戏文明时代的无限存档是如何实现的?

问题解决:

问题1.    设置要求:
    1.1.首先要有默认设置的数据
    1.2.对数据不仅需要能直接读,还要能够修改(实现删减功能)
    1.3.修改后的数据能够恢复默认设置

    1.首先应该对应设置创建一个单独的.dat文件,在其中按默认固定好的格式录入好数据(初始化)


    创建设置界面类,每一个界面按钮都对应相应的类的成员(定义类)

typedef struct GameSet_Struct
{
    struct
    {
        int FPS;
    }FPS;
    
    struct
    {
        struct
        {
            int Volume;
        }Role;

        struct
        {
            int Volume;
        }BackGround;

        struct
        {
            int Volume;
        }Skill;
    }Volume;

    struct
    {
        int State;
    }WindowFollow;
}GameSet_Struct;

图2

图2为定义的设置类


    2.鼠标点击按钮,按钮对应对象的成员,知道是类中的哪个成员被修改了,然后从当前设置(CurrentSet.dat)中拷贝出来,存储在由 设置类 定义的缓冲对象(SetBuffer)中。

GameSet_Struct* SetBuffer = (GameSet_Struct*)malloc(sizeof(GameSet_Struct));

把需要修改的成员的当前数据放入处理函数,处理函数处理完之后返回数据到缓冲区(SetBuffer)与相应的成员进行替换。替换之后的数据再次放入设置的控制文件中,完成修改。

综上所述:定义控制文档需要采用:“rb”读取当前控制文档的数据,然后将CurrentSet.dat的数据存储在缓冲(SetBuffer)中。
    在接受到指令之后。将缓冲区中的数据按照指令进行处理。
    然后关闭之前的“rb”类型文件,以“wb”的形式将之前文件中的数据删除,然后将缓冲区(SetBuffer)的数据导入到“wb”文件中。然后关闭“wb”文件。
    然后继续以“rb”文件的形式打开文件。继续读取设置文件中的数据。

3.创建好一个备用的文件,将最开始默认的初始值全部存放在文件中,
    只要触发特定条件,就将文件中的数据拷贝过去。(拷贝)
完成重置初始化。

另外思考:

打开文件只提供了添加和全部删除功能,如何实现修改指定数据的功能?

如上虽说:定义一个确定的类,按照类的格式输入到文件当中。

需要修改的时候,先将原来的数据全部拷贝到缓冲区,然后将缓冲区的数据放在修改函数。修改函数会根据用户指定要修改的内容进行处理,最后输出覆盖缓冲区数据。最后用"wb"打开文件删除之前所有的内容,将缓冲区的数据全部拷贝进去,实现修改数据。

问题2:人物数据要求:
1.整个人物数据从游戏中直接获取,获取之后替换原本的游戏数据
2.要求设置存档,不同存档中要查询大致数据。存档还要知道是否为空

    解决问题:
    1.创建人物数据类,把人物全部关键信息全部录入进去。
    在进行游戏的时候,把人物数据控制文件以rb的形式打开。
    在修改人物信息的时候,创建人物信息缓冲对象。把人物的数据录入到人物信息缓冲区中。
    然后把人物数据控制文件的读取改为wb,直接用新数据覆盖老的数据。
    然后关闭wb文件,从新打开rb文件。继续游戏

    2.游戏存储:

     方法1:首先提前设置几个存档,创建一个存档类,存档类里面有记录判断当前文档是否是空文档的参数
    以此来判断文档是否为空,是否可以存储人物数据。
    方法2:不需要预先存储几个存档,可以无限多开存档,
    设计一个以wb+的存档创建函数,直接创建新的存档。存档存档玩之后还能继续读取。
    然后在文件类中注册这个文件,表示这个文件不是空文件

问题3:

    分析:首先创建一个局数类:

    其中成员包括各种关于局数的数据以及当前是第几局
    每次开始新的游戏就先使用“ab+”来打开文件,(没有能够创建,有不会覆盖,还可以读取)
    把每局的数据全部录入到 局数类 创建的缓冲区;然后通过函数处理之后存入文件
    读取也是一样的
    从文件中按照 局数类 创建的缓冲区的格式,然后读取的时候全部导入到缓冲区
    最后在成功之后将存储的逐个释放到缓冲区,缓冲区的数据经过处理之后展现在屏幕上。
    特别注意,控制文件指针位置。不要关闭文件,防止文件指针丢失导致数据不能连贯。
    
    附加问题:如果需要跳过特定局数怎么办呢?

    方法1:提前定义:

FILE* fp;

typedef struct GameRound
{
  ......(//略)  
}GameRound;

#define     Size_Round    sizeof(GameRound) 

//一局字节长度

int SkipRoundNum = 1;

fseek(fp,SkipRoundNum*1L*Size_Round,SEEK_CUR);

//跳过指定局数

    
    方法2:得到当前拥有组数:N;
    首先用文件指针函数fseek(file,0L,SEEK_END);移动到文件最后位置。
    使用ftell(file)函数获取当前文档的总长度ALLLong;
    ALLLong/N = 一个单元的长度。

    就能够实现文件整段跳跃。
    

综上,一个文件file最好记录一类数据

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值