我试图修改文件中的少量记录。但是,这些更改没有反映出来。
我在学生roll number
的基础上搜索记录。
这是一段代码:
rewind(p);
printf("\nenter the roll no after which u wana enter rec\n");
scanf("%d",&val);
printf(" The record is\nrollno name tmks\n_________________\n");
while((fscanf(p,"%d%d%s%d",&i,&rno,name,&tmks))==4)
{
if(rno==val)
{
printf("%d | %s | %d \n",rno,name,tmks);
printf("\nenter rollNO totalMKS and name\n");
scanf("%d",&rno);
scanf("%d",&tmks);
scanf("%s",&name);
fprintf(p,"%d %d %s %d\n",i+1,rno,name,tmks);
break;
}
}
我已成功添加记录。但新插入并未反映上述代码中的更改。
问题出在哪里? 对不起老编译器,turbo c(它的强制性)。 这是 SSCCE :
#include<stdio.h>
#include<process.h>
void main()
{
FILE *p;int rno,tmks,i=1,val,ofset=0;char name[20],ch;
clrscr();
p=fopen("sturecord.txt","a+");
if(p!=NULL)
{
while(1)
{
printf("do u wish to enter the record? (y/n)");
ch=getch();
if(ch=='n')
{
break;
}
else
{
printf("\nenter rollNO totalMKS and name\n");
scanf("%d",&rno);
scanf("%d",&tmks);
scanf("%s",&name);
fprintf(p,"%d %d %s %d\n",i,rno,name,tmks);
i++;
}
}
}
rewind(p);
printf("\nenter the roll no after which u wana enter rec\n");
scanf("%d",&val);
printf(" The record is\nrollno name tmks\n_________________\n");
while((fscanf(p,"%d%d%s%d",&i,&rno,name,&tmks))==4)
{
if(rno==val)
{
printf("%d | %s | %d \n",rno,name,tmks);
printf("\nenter rollNO totalMKS and name\n");
scanf("%d",&rno);
scanf("%d",&tmks);
scanf("%s",&name);
fprintf(p,"%d %d %s %d\n",i+1,rno,name,tmks);
break;
}
}
fclose(p);
getch();
}
答案 0 :(得分:3)
关于您对fopen
的电话:
p = fopen ("sturecord.txt", "a+");
如果您以追加模式打开文件,无论您做什么,所有写入都将在文件的末尾。来自ISO C11 7.20.5.3 The fopen function /6
:
打开带有追加模式的文件('a'作为mode参数中的第一个字符)会导致对文件的所有后续写入强制转换为当前的文件结束,无论是否对fseek函数进行干预调用
通常,为了更改文件的任意部分,您可以在r+
模式下打开它,因为这允许您在文件中的任何位置进行读写,假设您遵循有关读取和刷新之间的刷新/搜索的规则写在上述ISO标准部分的/7
中。
但请记住,如果该文件尚不存在,则此r+
模式的开头将失败。您通常可以捕获它,然后尝试使用w+
打开它,但如果其他人也可能尝试创建该文件,您应该留意竞争条件的可能性。如果您是唯一期望创建该文件的程序,那么它通常是一个可行的选择。
另请注意,实际上将数据插入文件中没有标准机制。所有写入都将覆盖该位置已有的任何数据。
如果您想要实际插入数据,您有几个选择。
一种是在写入现在无用的区域之前实际将文件中的现有数据向上移动。这不是那么有效,因为它通常涉及许多读写操作,通常是在记录级别。
另一种方法是读取当前文件并将其写入新文件(按顺序)并在到达正确位置时“插入”新数据。然后删除旧文件并重命名新文件。同样,这可能不是最优的,尽管它往往比前一段更加优化,因为在阅读和写作期间你倾向于使用更大的块。
第三种方式可以远更有效率。它涉及将您的文件视为数据结构(例如,链接列表),以便您可以将信息附加到物理文件,同时将其视为已插入(在到逻辑文件)。这样就不需要对数据进行混洗,但是在读入或处理文件时会花费更多的工作。
扩展数据结构方法,假设您正在创建一个需要存储记录(有效负载)的程序,但您需要能够随意插入和删除这些记录。我们首先为每条记录创建一个结构(伪代码):
union tRecord:
structure firstRec:
uint32 firstRecNum
uint32 lastRecNum
uint32 firstFreeRecNum
# Unused space for uint8[116]
structure otherRec:
uint32 prevRecNum
uint32 nextRecNum
uint8 payload[120]
我们将此作为联盟,因为我们基本上将文件的第一部分视为元数据,包含有关第一个和最后一个逻辑记录以及第一个空闲记录的详细信息。
除了文件的第一部分之外,每个非空闲记录都包含前一个和下一个记录的记录号以及有效负载本身。由于没有记录号0(它是为元数据保留的),我们可以使用该记录号来表示列表的结尾。将记录号转换为物理文件位置只需将其乘以联合大小(在本例中为128)。
因此,“空”文件由以下内容组成(???
表示不相关的数据):
Byte offset Length Description
----------- ------ -----------
Meta-record
0 4 First record in file = 0
4 4 Last record in file = 0
8 4 First free record = 0
12 116 ???
End of file
128
使用此方案,您可以通过从firstRec.firstRecNum
中找到的第一条记录开始按顺序处理逻辑文件,并按照每条记录的otherRec.nextRecNum
字段形成的链进行操作在otherRec.payload
当你去(当链最终给你记录号0时停止)。
当插入记录时,您只需执行与内存中双向链接列表相同的操作。首先,您需要一个新记录,可以从当前空闲列表中提取,也可以将其物理添加到文件末尾。
然后,您只需调整文件中的指针条目,即可有效地将该记录插入逻辑顺序,无论其物理位置如何。没有涉及物理数据的混乱,只是覆盖已经存在的数据。
同样,删除逻辑记录不涉及将其从物理文件中删除。您只需调整指针,使其在非自由链中被绕过,并将其返回到空闲列表。请注意,空闲列表只需要是单个链接列表,因为它可以用作堆栈,始终在前面推送和弹出项目(免费记录的顺序无关紧要)。
当然,对于编辑现有记录的代码没有任何变化 - 只需覆盖有效负载部分并保留指针。
因此,添加四个记录然后删除中间记录所产生的文件内容将是:
Byte offset Length Description
----------- ------ -----------
Meta-record
0 4 First record in file = 1
4 4 Last record in file = 4
8 4 First free record = 2
12 116 ???
Physical record number 1 (first logical record)
128 4 Previous record number = 0
132 4 Next record number = 4
136 120 Payload 1
Physical record number 2 (first free record)
256 4 Previous record number = ???
260 4 Next record number = 3
264 120 ???
Physical record number 3 (second and last free record)
384 4 Previous record number = ???
388 4 Next record number = 0
392 120 ???
Physical record number 4 (second and last logical record)
512 4 Previous record number = 1
516 4 Next record number = 0
520 120 Payload 4
End of file
640