f.seek()在Python中的复杂性

时间:2018-08-11 15:34:47

标签: python file io big-o fseek

f.seek(500000,0)是否在到达第500000个字符之前先检查文件的所有前499999个字符? 换句话说,f.seek(n,0)是O(n)还是O(1)阶?

2 个答案:

答案 0 :(得分:10)

您需要更详细地说明对象f是什么类型。

如果f是存储在磁盘上的文件的常规io module对象,则必须确定是否要处理:

  • 原始二进制文件对象
  • 缓冲对象,包装原始二进制文件
  • 一个TextIO对象,包装缓冲区
  • 内存BytesIOTextIO对象

第一个选项仅使用lseek system call来重新定位文件描述符的位置。如果此调用为O(1),则取决于操作系统以及所拥有的文件系统类型。对于具有ext4文件系统的Linux系统,请lseek is O(1)

缓冲区仅清除缓冲区if your seek target is outside of the current buffered region并读入新的缓冲区数据。也是O(1),但是固定成本更高。

对于文本文件,事情变得更加复杂,因为可变字节长度的编解码器和行尾转换意味着您无法始终将二进制流位置映射到文本位置,而无需从头开始进行扫描。该实现不允许使用非零的当前位置或相对终点搜索,并且最好最大程度地减少为绝对搜索读取的数据量。 Internal state shared with the text decoder跟踪recent 'safe point' to seek back to,并向前读取到所需位置。最坏的情况是O(n)。

内存文件对象实际上只是很长的可寻址数组。寻求O(1)是因为您可以更改当前位置指针值。

还有许多其他文件状对象可能支持也可能不支持搜索。他们如何处理寻求取决于实现。

  • zipfile module支持查找以只读模式打开的zip文件,并且查找到当前缓冲区所覆盖的数据段之前需要完全重新读取和解压缩的点之前的一点。数据到所需的点,要进行搜索,需要从当前位置读取直到找到新位置。 gziplzmabz2模块都使用相同的共享实现,如果您要搜索到当前读取位置之前的一点(并且没有更大的缓冲区),它也将从头开始读取为了避免这种情况)。

  • chunk module允许在块边界内搜索并委托给基础对象。如果基础文件查找操作为O(1),则这是O(1)操作。

等等因此,这取决于

答案 1 :(得分:1)

这取决于f的实现。但是,在普通文件系统文件中,它是O(1)。

如果python在文本文件上实现f,则可以将其实现为O(n),因为可能需要检查每个字符才能正确管理cr / lf对。

  • 这将基于f.seek(n,0)是否给出与读取字符循环相同的结果,并且(取决于操作系统)将cr / lf缩小为lf还是将lf扩展为cr / lf

如果python在压缩流上实现f,则该顺序为b O(n),因为解压缩可能需要对块进行一些工作并进行解压缩。