更改文件部分内容(1)
本文使用
c
语言实现更改文件部分内容
更改一个已经存在的文件的部分内容是比较困难的,原因是对已经存在的文件进行的操作都是覆盖操作,没有插入和删除操作,因此理论上来说,你只能够增大或者维持文件大小不变,而不能缩小文件,要实现更改文件部分内容的目标,共有两种方法:创建临时文件作为中转、创建缓冲区作为中转
创建临时文件作为中转
使用到的函数
-
FILE* fopen(const char* fileName, const char* mode)
需要头文件
#include<stdio.h>
fopen()
打开文件,如果用的是w+
mode的话,当打开一个已存在的文件,会清空其中内容;如果文件不存在,会创建一个空白文件,符合我们创建临时文件的要求。创建临时文件:
fopen(fileName,"w+")
要打开修改的文件,就不能用
w+
mode,因为会清空内容。可以选用r+
或者r
来进行操作。打开修改文件:
fopen(fileName,"r+")
或者fopen(fileName,"r")
-
int _access(const char* path, int mode)
需要头文件
#include<io.h>
注意这个函数是windows下检验的函数,如果要linux下检验的函数,对应的是
access
。这个函数可以检验文件是否存在,只要设置
mode=00
就可以。如果文件存在,返回0,文件不存在,返回-1。这个是用来和创建临时文件相互配合,创建临时文件之前先判断当前目录中是否有同名文件,避免覆盖有用的文件内容。
-
int fgetc(FILE* stream)
需要头文件
#include<stdio.h>
这个函数返回
stream
对应的文件中的一个字符,同时将文件流的指针后移。利用这个函数可以对文件中的字符进行逐个遍历。
-
char* fgets(char* str, int num, FILE* stream)
需要头文件
#include<stdio.h>
这个函数读取一行数据或者是最多
num-1
个字符,放入str
中,注意,该函数会自动添加尾0。同时该函数只有当输入遇到问题的时候,返回值才会为
NULL
,就算这次读入遇到了文件终止符,只要成功读入了内容,那么返回值就不会为NULL
,因此可以用fgets() == NULL
来判断读取全部有用信息。 -
int fputc(int character, FILE* stream)
向
stream
代表的文件流中输入一个字符,同时文件指针自动前移。 -
int fprintf(FILE* stream, const char* format, ...)
这个函数类似于正常的
printf
,只是前面多了一个FILE*
参数,表示是向对应的文件进行输入。 -
size_t fread(void* ptr, size_t size, size_t count, FILE* stream)
这个函数从文件中读取
count * size
的字节数内存,存入ptr
中,注意,该函数读取的字节不会自动附加尾零。该函数通常用于整体读取
-
size_t fwrite(void* ptr, size_t size, size_t count, FILE* stream)
这个函数用于向文件中存储
count * size
的字节数内存,从ptr
中获取,同样的也不会因为尾零而终止。该函数通常用于整体写入
-
int remove(cont char* filename)
删除文件
filename
,用于删除待修改的文件 -
int rename(const char* oldname, const char* newname)
把
oldname
文件名称改为newname
,用于把临时文件改为原来的文件名称
实现逻辑
创建临时文件
File* create_tmp() //创建临时文件(只是为了方便写成了个函数,真正操作的时候要放在主函数中,因为要用到临时文件名称)
{
do //生成合适的临时文件名称
{
char tmp[200]; //创建一个200个字节的长度存储临时文件名
for(int i = 0; i < 100; i++)
tmp[i] = rand() % 26 + 'a'; //用随机数创建一个文件名,保证尽可以能不重复
tmp[100] = '\0';
strcat(tmp,".tmp"); //给临时文件赋一个后缀。
} while(access(tmp,0) >= 0) //只要当前文件名存在,就重新生成
File* fp = fopen(tmp,"w+"); //如果fp为NULL,说明创建或者打开失败,这个可以留给主函数判断
return fp;
}
对修改文件进行处理
这里以清除注释为例,假设#
后面的都是注释,那么我想把注释内容全部删掉,就可以一行一行的进行处理
void check_file(const char* fileName) //对文件内容进行处理
{
FILE* fp, *tmpFp; //创建两个文件指针,fp指向待修改文件,tmpFp指向临时文件。
fp = fopen(fp,"r");
if(fp == NULL) //待修改文件打开失败
{
printf("文件%s打开失败\n",fileName);
return; //程序终止
}
do //生成合适的临时文件名称
{
char tmp[200]; //创建一个200个字节的长度存储临时文件名
for(int i = 0; i < 100; i++)
tmp[i] = rand() % 26 + 'a'; //用随机数创建一个文件名,保证尽可以能不重复
tmp[100] = '\0';
strcat(tmp,".tmp"); //给临时文件赋一个后缀。
} while(access(tmp,0) >= 0) //只要当前文件名存在,就重新生成
tmpFp = fopen(tmp,"w+"); //如果fp为NULL,说明创建或者打开失败,这个可以留给主函数判断
if(tmpFp == NULL) //临时文件创建失败判断
{
printf("临时文件创建失败\n");
return;
}
char lineStr[1024]; //假设一行最多1024字节
while(fgets(lineStr,1024,fp)) //对文件内容进行处理
{
get_index(lineStr); //假设get_index删除lineStr中#之后内容
fprintf(tmpFp,"%s",lineStr); //将更改后的内容加入临时文件之中
} //持续此操作,直到文件中内容全部被遍历
fclose(fp); //把两个文件指针关闭
fclose(tmpFp);
remove(fileName); //把原来的文件删除
rename(tmp,fileName); //把临时文件更改为源文件的名称
}