gpt4 book ai didi

c - fseek/循环倒带

转载 作者:行者123 更新时间:2023-12-04 11:31:19 29 4
gpt4 key购买 nike

我在代码中遇到一种情况,其中有一个巨大的功能,可以逐行解析记录,验证并写入另一个文件。

如果文件中有错误,它将调用另一个函数来拒绝记录并写入拒绝原因。

由于程序中的内存泄漏,它会随着SIGSEGV崩溃。一种从崩溃位置“重新启动”文件的解决方案是将最后处理的记录写入一个简单文件。

为此,需要将处理循环中的当前记录号写入文件。如何确保循环中的数据被覆盖?

使用fseek在循环中首先定位/倒带是否会降低性能?

记录的数量有时可能很多(最多500K)。

谢谢。

编辑:内存泄漏已得到修复。建议重新启动解决方案为
额外的安全措施和手段以及SKIP n记录解决方案提供重启机制。对不起,您之前没有提到它。

最佳答案

遇到此类问题时,可以采用以下两种方法之一:


建议的方法:对于每个读取的记录,将记录号(或输入文件中ftell返回的位置)写到单独的书签文件中。为确保完全从上次中断的地方恢复,以免引入重复的记录,每次写​​入(对fflush和输出/拒绝文件都必须)后必须bookmark。通常,此操作和无缓冲的写入操作都会减慢速度典型(无故障)场景。为了完整起见,请注意您有三种写入书签文件的方式:


fopen(..., 'w') / fwrite / fclose-非常慢
rewind / truncate / fwrite / fflush-快一点
rewind / fwrite / fflush-更快一些;您可以跳过truncate,因为记录号(或ftell位置)将总是与上一个记录号(或ftell位置)一样长或更长,并且将完全覆盖它,前提是您一次在以下位置截断了文件启动(这回答了您的原始问题)

假设在大多数情况下一切都会顺利;在故障后恢复时,只需计算已输出的记录数(正常输出加拒绝),然后从输入文件中跳过等效的记录数。


这样可以使典型的(无故障)场景保持极快的速度,而不会在出现故障后恢复的情况下大幅降低性能。
您不需要fflush文件,或者至少不需要那么频繁。在切换到写入拒绝文件之前,您仍然需要fflush主输出文件,而在切换回写入主输出文件之前,您仍然需要fflush在拒绝文件中(对于500k记录,可能需要数百或数千次)只需从输出/拒绝文件中删除最后一个未终止的行,直到该行的所有内容都将保持一致。



我强烈建议使用方法2。与方法2所需的任何其他(缓冲的)读取(fflush可能需要数毫秒;将其乘以500k并得到分钟数相比,方法1所引起的写入(三种可能性中的任一种)都非常昂贵) 500k记录文件中的行数仅需几秒钟,而且,文件系统缓存正在使用,而不是对此不利。)



编辑
只是想阐明实现方法2所需的确切步骤:


当分别写入输出和拒绝文件时,仅在从写入一个文件切换到写入另一个文件时才需要刷新。请考虑以下情形,以说明进行这些文件刷新的必要性:


假设您将1000条记录写入主输出文件,则
您必须向拒绝文件写入1行,而无需先手动刷新主输出文件,然后
您将200多行写入主输出文件,而无需先手动刷新拒绝文件,然后
运行时会自动为您刷新主输出文件,因为您已经在主输出文件的缓冲区中积累了大量数据,即1200条记录


但是运行时尚未为您自动将拒绝文件刷新到磁盘,因为文件缓冲区仅包含一条记录,该记录不足以自动刷新

您的程序在此时崩溃
您可以恢复并计数主输出文件中的1200条记录(运行时会为您清除这些记录),但是拒绝文件中的0(!)记录(未刷新)。
假设仅成功处理了1200条记录到主输出文件,则在记录#1201处继续处理输入文件;被拒绝的记录将丢失,并且第1200个有效记录将被重复
你不要这个!

现在考虑在切换输出/拒绝文件后手动刷新:


假设您将1000条记录写入主输出文件,则
您遇到一条无效记录,该记录属于拒绝文件;最后一条记录有效;这意味着您将切换到写入拒绝文件:在写入拒绝文件之前刷新主输出文件
您现在将1行写入拒绝文件,然后
您遇到一条属于主输出文件的有效记录;最后一条记录无效;这意味着您将切换到写入主输出文件:在写入主输出文件之前刷新拒绝文件
您将200多行写入主输出文件,而无需先手动刷新拒绝文件,然后
假设运行时没有为您自动刷新任何内容,因为自上次对主输出文件进行手动刷新以来,缓冲的200条记录不足以触发自动刷新
您的程序在此时崩溃
您可以在主输出文件中恢复并计数1000条有效记录(在切换到拒绝文件之前手动刷新了记录),在拒绝文件中记录了1条记录(在切换回主输出文件之前手动刷新了记录)。
您可以正确地恢复处理记录#1001处的输入文件,该记录是无效记录之后的第一个有效记录。
您会重新处理接下来的200条有效记录,因为它们没有被刷新,但是没有丢失的记录,也没有重复的记录

如果您对运行时的自动刷新之间的间隔不满意,也可以每100或每1000条记录进行一次手动刷新。这取决于处理记录是否比刷新更昂贵(如果处理更昂贵,则应在每个记录之后频繁刷新,否则仅当在输出/拒绝之间切换时才刷新)。
从失败中恢复


打开输出文件和拒绝文件以进行读取和写入,然后从读取和计数每个记录(例如records_resume_counter)开始,直到到达文件末尾
除非您在输出每条记录后都进行刷新,否则还需要对输出和拒绝文件中的最后一条记录进行一些特殊处理:


从中断的输出/拒绝文件中读取记录之前,请记住您在该输出/拒绝文件中的位置(使用ftell),我们将其称为last_valid_record_ends_here
阅读记录。验证该记录不是部分记录(即运行时尚未将文件刷新到记录的中间)。
如果每行有一条记录,可以通过检查记录中的最后一个字符是回车符还是换行符(\n或`r`)轻松地进行验证


如果记录已完成,请增加记录计数器并继续下一条记录(或文件结尾,以先到者为准)。
如果记录是不完整的,则将fseek返回到last_valid_record_ends_here,并停止从此输出/拒绝文件读取;不要增加计数器;继续进行下一个输出或拒绝文件,除非您已遍历所有这些文件


打开输入文件进行读取,并从中跳过records_resume_counter条记录


继续处理并输出到输出/拒绝文件;这将自动附加到输出/拒绝文件,您无需在其中读取/计数已处理的记录
如果必须对部分记录刷新执行特殊处理,则输出的下一条记录将覆盖上一次运行(在last_valid_record_ends_here处)的部分信息-您将没有重复,垃圾或丢失的记录。

关于c - fseek/循环倒带,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/605247/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com