T O P

[资源分享]     C语言:在文件的指定位置实现局部修改,而无需重写文件的其他部分

  • By - 楼主

  • 2019-12-16 12:16:39
  • C语言:在文件的指定位置实现局部修改,而无需重写文件的其他部分

    前言(可跳过)

    在进行大型文件的读写操作时,若采用读打开文件a,再将“a中修改删除位置之前的内容+修改删除的内容+a中修改删除位置之后的内容”保存到文件b,关闭并删除文件a,将文件b改名为与文件a同名的简单办法,即使结果同修改指定位置的数据的方式相同,但程序所占时间片和内存空间都会难以想象的巨大
    作为一个优秀的程序员,这种掩耳盗铃的办法根本不是应该采取的办法,采用追加写入然后替换源文件的方式根本不能代替对指定位置局部修改的功能,我们必须直面问题,怎么将数据写入指定位置,这不是PTA黑盒测试,不能只关注结果。纵使结果相同,但只要实现方式不正确,问题就还没解决。
    然而,我查看许多平台上许多相似提问的回答,大多人都采用一些代替方法来解决问题,这并不值得苛责,闻道有先后,术业有专攻,每个程序员都有他们自己不会的内容,这很正常,甚至我的水平比看这篇文章的大多数人都要来得差。但问题是,知之为知之,不知为不知,不会就是不会,不会就不要自负地觉得自己的办法是唯一的办法。有的人只会那些代替办法,还信誓旦旦说没有更好的办法了,我之前差点就以为只有这些替代办法了,没有办法直接对指定文件进行修改。承认自己能力不足很难吗,可能对某些人来说很难,但是不管怎么说,自己能力不足不愿听取他人意见还自负地去误人子弟是不对的。
    当然,我也能力也远远不足,以下我所采用的办法也不是最好的办法,就权当我抱砖引玉,希望我的方法能对各位读者有所启发

    具体需求

    不同的人有不同的需求,我是在处理二进制文件时所产生的需求,我要求对该二进制文件的制定位置进行读写操作,且要求在未找到该文件的情况下新建文件。
    我在查找解决问题的方法中,看到有些人的需求是在文本文件中查找指定数据并修改,在此也一并解决。

    解决思路

    有些东西解决起来感觉很难,但一旦看到解决办法,啊,就感觉真的好简单。
    就像那经典的程序员语句:“卧槽,程序还能这么写!”
    这里的思路实现起来其实很简单,但是为什么我当初想了那么久才想到呢T_T
    我们要注意到,在C语言标准I/O(输入与输出)库中,fopen()函数的模式字符串如下:
    fopen()函数的模式字符串我们发现w写入会清空原文件,a写入只能追加,而+号代表可以读和写,所以其实我们只要采用r+型就可以做到对指定位置的输入了。(看来语文真的很重要)
    注意,一定要是r+型(含rb+,rt+)任何其他方式都不行
    事实上,ANSI C标准库一直提供更新文件的方式,不过我一直没注意到r+方式也可以写入,并且还是可以在文件中修改删除的那种方式,真的是踏破铁皮无觅处,得来全不费工夫。
    不过我的需求还差一点,那就是文件不存在还需新建一个,对此我采用了初始化检查中插入a+的方式。

    解决代码

    注意:在VS2019环境下的部分实现代码

    #include<stdio.h>//由于iostream包含stdio.h,体积过于庞大,故不采用
    bool Examination() {//检查函数
    	FILE* test;//设置文件指针
    	errno_t open_return;//文件打开返回值,若返回非0则失败
    	open_return = fopen_s(&test, "data", "ab+");//以二进制打开程序目录下的data文件,若无则新建
    	fclose(test);//检索成功,关闭文件
    	return (bool)open_return;//检查正常
    }
    int main() {
    	char Exam_Return = Examination();//检查data文件
    	if (Exam_Return) {}//返回1则检查失败
    	FILE* data;//设置文件指针
    	errno_t open_return;//文件打开返回值,若返回非0则失败
    	open_return = fopen_s(&data, "data", "rb+");//以读写二进制打开程序目录下已存在的data文件
    	Exam_Return = fclose(data);//成功关闭返回0
    	if (Exam_Return) {}//文件关闭失败
    	return 0;
    }
    

    注意,VS中的文件函数同其他编译器不同非VS环境下

    #include<stdio.h>//由于iostream包含stdio.h,体积过于庞大,故不采用
    bool Examination() {//检查函数
    	FILE *test;//设置文件指针
    	test = fopen("data", "ab+");//以二进制打开程序目录下的data文件,若无则新建
    	fclose(test);//检索成功,关闭文件
    	return 0;
    }
    int main() {
    	char Exam_Return = Examination();//检查data文件
    	if (Exam_Return) {}//返回1则检查失败
    	FILE *data;//设置文件指针
    	data = fopen("data", "rb+");//以读写二进制打开程序目录下已存在的data文件
    	Exam_Return = fclose(data);//成功关闭返回0
    	if (Exam_Return) {}//文件关闭失败
    	return 0;
    }
    

    以上只是初始化的代码,不包含输入输出功能,各位读者可以根据自己的需要在此基础上进行相应的增减。
    比如我所写入文件的数据为结构体(类),以下是部分输入代码(VS2019环境下):

    //结构体指针为Present,size为结构体大小
    	function_return=fseek(data, (Present->num - 1) * size, SEEK_SET); //指针移至修改目标前
    	if (function_return != 0) {//fseek返回非0表明错误
    		rewind(data);//重置文件指针
    		return 1;
    	}
    	function_return = fwrite(Present, size, 1, data);//注意:在a+方式下fwrite函数从文件末尾开始写入,而在上述的r+方式下,fwrite可以在文件中写入
    	rewind(data);
    	if (!function_return) { printf("写入失败\n"); return 1; }//失败返回0值
    	printf("写入成功\n");
    	return 0;
    

    以下是部分输出代码:

    bool Load(FILE* u, struct person* n, int x) {//根据编号查找并加载文件指定结构体,若失败则返回1
    	rewind(u);
    	int function_return = 0;
    	if (x < 1) { printf("输入无效\n"); return 1; }//若n小于1,返回0,当n=1时,查找编号为1的结构体,即第0(n-1)个
    	function_return = fseek(u, (x - 1) * size, SEEK_SET);//成功返回0值
    	if (function_return) { printf("查找失败\n"); return 1; }//失败返回非0值
    	printf("查找成功\n");
    	function_return = fread(n, size, 1, u);//成功返回非0值
    	if (!function_return) { printf("读取失败\n"); return 1; }//失败返回0值
    	printf("读取成功\n");
    	Output_Structure(n);
    	rewind(u);
    	return 0;
    }
    bool Output_Structure(struct person* n) {//结构体输出函数
    	printf("\n--------------------\n");
    	printf("编号:%d\n", n->num);
    	printf("姓名:%s\n", n->name);
    	printf("情况说明:%s\n", n->circumstance);
    	printf("相似人物姓名:%s\n", n->related_name);
    	printf("相似人物编号:%d\n", n->related_number);
    	printf("相似原因:%s\n", n->reason);
    	printf("人物评价:%s\n", n->comment);
    	printf("--------------------\n");
    	return 0;
    }
    

    实验结果

    经实验,上述代码可以实现在文件中间插入
    实验结果:
    查找1的结果:
    查找1的结果
    查找2的结果:
    查找2的结果
    修改1的结果:
    写入1的结果
    再次查找1的结果:
    再次查找1的结果
    再次查找2的结果:
    在这里插入图片描述
    可以看到,1被修改了,而2没有发生变动。
    如此,便通过C语言实现了对文件指定位置数据的修改

    接下来,再尝试对文件的追加写入:
    查找10的结果:
    查找10的结果
    写入10的结果:
    写入10的结果
    再次查找10的结果:

    再次查找10的结果
    对文件的追加写入成功实现。
    可以看到,在fopen()函数中采用r+方式打开文件,既可以在文件中修改数据,也可以追加写入,十分方便。

    反思与总结(可跳过)

    怎么说呢,我承认我可能有点思维僵化了。
    一直以来,我始终认为只有w和a格式的fopen()函数才可以输入,但是显然,r+格式也可以输入,而且是可以在文件中输入的那种。
    真没想到,读的扩展是可以输入的,想必那些知道这点的人也根本不会去回答那些如何用C语言在文件的指定位置输入的问题吧,因为这可以算是基本功的一部分了。
    这件事情给我的带来的教训就是不能忽视那些看起来根本无关的功能,也许那些函数或者功能就能解决我当下面临的最大难题呢,谁也未可知,不是吗?
    最后,希望这篇文章能够对各位读者有一点启发。如果这篇文章可以帮助你们解决一些问题,那我就十分满意了。
    如果还有读者对相应功能的实现还存在一些疑惑,或者文章还存有阐述不甚清楚的地方,欢迎大家在文章下方留言指点,我会在方便的时候回复的。

    其他需求的实现

    查找文本文件中特定文本并进行替换的部分实现代码(非VS环境下):

    #include<string.h>//加载字符串比较函数
    
    //fp为文件指针,size为结构体数据长度
    while(fread(Present,size,1,fp)==1){//每次读入一个结构体长度的数据,返回1则代表读取成功
    	if(strcmp(Present->name,"Test")==0){//如果Present所指向的结构体名字与文件中字符串配对成功
    		fseek(fp,-size,SEEK_CUR);//指针移至修改目标前
    		fwrite(Present,size,1,fp);//将结构体中的内容写入文件
    		break;//退出循环
    	}
    }
    
    

    注意,此时的文件的打开方式应改为rt+型(Linux下或UNIX下不用)或者r+型,(反正不能是rb+型)如下所示:

    FILE *fp;//设置文件指针
    fp = fopen("data", "rt+");//以读写文本格式打开程序目录下已存在的data文件
    

    本帖子中包含资源

    您需要 登录 才可以下载,没有帐号?立即注册


Image

yyyyyyanq 1

这样的话能实现关键字查找功能么