当前位置:  开发笔记 > 程序员 > 正文

循环中的fseek /倒带

如何解决《循环中的fseek/倒带》经验,为你挑选了1个好方法。

我有一个代码中的情况,其中有一个巨大的函数,逐行解析记录,验证和写入另一个文件.

如果文件中存在错误,它会调用另一个拒绝记录的函数并写入拒绝原因.

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

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

使用fseek首先在循环中定位/回放会降低性能吗?

记录的数量可以很多,有时(高达500K).

谢谢.

编辑:内存泄漏已得到修复.建议将重启解决方案作为额外的安全措施,并提供重启机制以及SKIP n记录解决方案.很抱歉没有提到它.



1> vladr..:

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

    您建议的方法:对于您阅读的每条记录,将记录号(或ftell输入文件上返回的位置)写出到单独的书签文件中.为了确保你从中断的地方恢复,不要引入重复的记录,你必须fflush在每次写入之后(对两者bookmark和输出/拒绝文件).这种和无缓冲的写操作一般会减慢典型(无故障) )场景显着.为了完整起见,请注意您有三种写入书签文件的方法:

    fopen(..., 'w') / fwrite / fclose - 非常慢

    rewind / truncate / fwrite / fflush - 略快一点

    rewind / fwrite / fflush- 有点快 ; 您可以跳过,truncate因为记录号(或ftell位置)将始终与前一个记录号(或ftell位置)一样长或长,并且将完全覆盖它,前提是您在启动时截断文件一次(这将回答您的原始问题)

    假设大多数情况下一切顺利; 在失败后恢复时,只需计算已输出的记录数(正常输出加拒绝),并从输入文件中跳过相同数量的记录.

    这样可以非常快速地保持典型(无故障)方案,而不会在故障后恢复情况下显着降低性能.

    您不需要fflush文件,或者至少不需要这样.fflush在切换到写入fflush拒绝文件之前,仍然需要主输出文件,然后在切换回写入主输出文件之前拒绝文件(对于500k记录输入可能需要几百或几千次.)只需删除从输出/拒绝文件的最后一个未终止的行,到该行的所有内容将是一致的.

我强烈推荐方法#2.与方法#2所需的任何附加(缓冲)读取相比,方法#1所引入的写入(三种可能中的任何一种)都非常昂贵(fflush可能需要几毫秒;乘以500k并得到分钟 - 而计算数量500k记录文件中的行只需几秒钟,而且文件系统缓存正在使用,而不是针对您.)


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

当写入输出并分别拒绝文件时,只需要在从写入文件切换到写入另一个文件时进行刷新.请考虑以下场景,说明执行这些文件刷新切换的必要性:

假设您将1000条记录写入主输出文件,然后

你必须在拒绝文件中写入1行,而不是先手动刷新主输出文件

你再向主输出文件写200行,而不是先手动刷新拒绝文件

运行时会自动刷新主输出文件,因为您在主输出文件的缓冲区中累积了大量数据,即1200条记录

但是运行时还没有自动将拒绝文件刷新到磁盘,因为文件缓冲区只包含一条记录,这个记录不足以自动刷新

你的程序此时崩溃了

你恢复并计算主输出文件中的1200条记录(运行时为你刷出了这些记录),但拒绝文件中的0(!)记录(未刷新).

您继续处理记录#1201的输入文件,假设您只有1200条记录成功处理到主输出文件; 被拒绝的记录将丢失,并且将重复第1200个有效记录

你不想要这个!

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

假设您将1000条记录写入主输出文件,然后

您遇到一个属于拒绝文件的无效记录; 最后一条记录是有效的; 这意味着您正在切换到写入拒绝文件:在写入拒绝文件之前刷新主输出文件

你现在写1行到rejects文件,然后

你遇到一个属于主输出文件的有效记录; 最后一条记录无效; 这意味着您将切换到写入主输出文件:在写入主输出文件之前刷新rejects文件

你再向主输出文件写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从中跳过记录

继续处理并输出到输出/拒绝文件; 这将自动附加到输出/拒绝文件,在该文件中,您不会读取/计算已处理的记录

如果必须对部分记录刷新执行特殊处理,则输出的下一条记录将覆盖上一次运行(at last_valid_record_ends_here)的部分信息- 您将没有重复,垃圾或丢失记录.

推荐阅读
乐韵答题
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有