更改c语言程序保存地址吗,C语言文本文件实现局部修改

前言:以往进行文件操作时,都是以链表(变量)作为用户所见数据与文件数据之间的桥梁。用户改变数据时,先改变链表(变量)中的数据,然后直接将整个链表重新写入文件,以做到“实时同步”,非正常退出程序一般数据也不会丢失。但是数据量很大时,微小的改动就要重写整个文件,浪费很多计算资源。但是在控制台程序中,没有C语言做不到的事,只有C语言不擅长的事。下面介绍一下文本文件的局部修改(覆盖写入)。当然,不进行覆盖的话即实现了插入

先来回顾一下二进制文件(任意读写文件、随机读写文件)的局部修改

下面这段程序向man.dat文件中写入三个人的名字,年龄

typedef struct {

char name[20];

int age;

} Data;

int main(){

Data man[3];

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

printf("please input %c's name:",i+'A');

scanf("%s",man[i].name);

printf("please input %c's age:",i+'A');

scanf("%d",&man[i].age);

}

FILE *fp = fopen("man.dat","wb");

fwrite(man,sizeof(Data),3,fp);

fclose(fp);

return 0;

}

下面这段程序进行了二进制文件的任意读写

typedef struct {

char name[20];

int age;

} Data;

int main(){

FILE *fp;

char name[100];

printf("请输入想要修改的人的名字:");

scanf("%s",name);

Data data;

fp = fopen("man.dat","r+");

while(fread(&data,sizeof(Data),1,fp)==1){

if(strcmp(data.name,name)==0){找到修改目标

printf("请输入新的名字:");

scanf("%s",name);

strcpy(data.name,name);修改变量data的name,同样这里也可以修改age

fseek(fp,-sizeof(Data),SEEK_CUR);指针移至修改目标前

fwrite(&data,sizeof(Data),1,fp);写入新的data覆盖原data

printf("修改成功\n");

break;

}

}

fclose(fp);

puts("开始读取");

Data test;

fp = fopen("man.dat","r+");

while(fread(&test,sizeof(Data),1,fp)==1)

printf("%s %d\n",test.name,test.age);

return 0;

}

文本文件局部修改示例程序在文章末尾

前提条件

待修改数据的数据长度最好已知

修改后的数据长度要比原来小或相同,最好相同

具有写入权限,文件打开方式为**r+**等

比如int类型的status字段表示该数据元素的使用状态,用1表示正在使用,0表示闲置。现在有改变这个状态的需求,那么这就非常符合本文所述的情况

主要流程

1.得到待修改的数据在文件中的位置

2.将文件内部指针定位到该位置

3.写入新数据到文件

首先 我们对fscanf等函数的细节需要有一些了解

fscanf所读数据间若存在换行符则读取时文件指针会自动跳过换行符,

而无需在格式化字符串中加上换行符,且换行符占两个字节(私以为是\r\n的缘故,ps:Windows环境下)

fscanf与scanf相似,数据间多余个空格(非格式化字符串间的空格)不造成读取影响

文本文件又称ASCLL码文件,顾名思义我们可以计算出某些位置的字节数

说明:本文所述偏移字节数为ASCLL文件中的字节数,即字符数

得到待修改的数据在文件中的位置

假设当前有data.txt内容为

0 1 2

3 4 5

现在我们要得到5的位置

int num;

FILE *fp = fopen("data.txt","rt+");

while(fscanf(fp,"%d",&num)!=EOF&&num!=5);

读取后文件指针指向5的后面

方法一

long ftell( FILE *stream );发生错误返回-1 利用ftell函数得到当前当前文件内部指针位置(相对文件首偏移字节数)

long position = ftell(fp);

position -= 待修改数据长度; 即得到要定位到的位置

方法二

int fgetpos( FILE *stream, fpos_t *position );fgetpos()函数保存给出的文件流(stream)的位置指针到给出的fpos_t类型的位置变量(pos)中.

(fpos_t在stdio.h中定义,实质上是一种__int64).fgetpos()执行成功时返回0,失败时返回一个非零值 利用fgetpos获得文件内部指针位置

fpos_t pos;

fgetpos(fp,&pos);

pos -= 待修改数据长度;

如果我们清晰的知道文件的存储结构,比如它存储的是大量相同的数据块(就像二进制文件那样),我们也可以遍历计数,然后计算出偏移字节数

将文件内部指针定位到该位置

//经试验,用于读取的文件指针无法用于写入

通过上述方法得到需要的文件指针位置(偏移字节数)后

接下来需要定位文件内部指针到该位置

int fseek( FILE *stream, long offset, int origin );

或者

int fsetpos( FILE *stream, const fpos_t *position );

feesk

其中,fseek函数中int origin参数的值应该是下列值其中之一

名称

说明

SEEK_SET(0)

从文件的开始处开始

SEEK_CUR(1)

从当前位置开始

SEEK_END(2)

从文件的结束处开始

fessk从orign处开始,向后偏移offset个字节数,offset < 0 即向前移动

offset 参数为 fgetpos获取到的fpos_t类型的pos也可,只需(long)pos

fsetpos

fsetpos用于将文件内部指针定位到pos,同理pos也可由ftell得到并转换为fpos_t

示例

将上述data.txt中的5修改为0

fseek(fp,position,0);/#0 == SEEK_SET 从文件首开始

特别地你可以不需要用ftell获得偏移字节数

fseek(fp,-待修改数值长度,SEEK_CUR);从当前位置开始

或者

fsetpos(fp,&pos);

//pos = (fpos_t) position;

//fset(fp,pos);

写入新的数据到文件

fprintf照常写入就好,此时会覆盖原数据(注意换行符),但是此时要注意此时文件指针的位置,不要做多余的写入,新数据长度不足要考虑用空格覆盖原数据,由于fscanf格式化字符串的特点,再读入时空格可以忽略。或者根据存储方式与程序数据定义的特点进行适当的写入

附上一些输入流操作函数

rewind(FILE *stream);回滚指针到文件流首

feof(FILE *stream);判断文件流是否到达文件尾,若到达返回一个非零值

FILE *freopen( const char *fname, const char *mode, FILE *stream );给一个已存在的流再分配一个不同的文件和方式(mode).在调用本函数后,给出的文件流将会用mode(方式)指定的访问模式引用fname(文件名).

freopen()的返回值是新的文件流,发生错误时返回NULL.

fflush();

示例

下面的程序将三个结构体变量写入man.txt文件

typedef struct{

char name[20];

int age;

}Data;

int main(){

Data man[3];

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

printf("please input %c's name:",i+'A');

scanf("%s",man[i].name);

printf("please input %c's age:",i+'A');

scanf("%d",&man[i].age);

}

FILE *fp = fopen("man.txt","w");

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

fprintf(fp,"%s %d\n",man[i].name,man[i].age);

}

fclose(fp);

return 0;

}

运行及输入

3ef6c31863dbec055333034048556c9e.png

创建的文件

b3e0f2b33bacd146f103b9d9fdf2fb4c.png

现在我们来修改第二行的16这个数据

修改程序如下

typedef struct{

char name[20];

int age;

}Data;

int main(){

FILE *fp = fopen("man.txt","r+");

Data data = {"liupeidong",16};//修改目标

char name[100];

int age;

while(fscanf(fp,"%s%d",name,&age)!=EOF){

if(strcmp(name, data.name) == 0 && age == data.age){找到目标

fseek(fp,-2,SEEK_CUR);回滚指针至16前

printf("please input new age:");

scanf("%d",&age);

fprintf(fp,"%d",age);覆盖写入

break;

}

}

fclose(fp);

return 0;

}

运行修改程序

e00b6ec6cfd3b7d6ec6fb56946cef204.png

我在Dev中打开了man.txt,然后使用程序修改了它,Dev的提醒

14704d8e2fb49fc609eee505ae1a998a.png

修改后的文件

1c03c94aff6f8355da3252199bd57d32.png

2018/12/21更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值