Python file.tell给出了错误的值位置

kto*_*tom 4 python seek tell

我试图使用Python从现有文件中提取许多位置.这是我当前提取位置的代码:

    self.fh = open( fileName , "r+")
    p = re.compile('regGen regPorSnip begin')
    for line in self.fh :
        if ( p.search(line) ):
            self.porSnipStartFPtr = self.fh.tell()
            sys.stdout.write("found regPorSnip")
Run Code Online (Sandbox Code Playgroud)

这个代码段重复了很多次(少了文件打开),具有不同的搜索值,似乎有效:我得到了正确的消息,变量有值.

但是,使用下面的代码,第一个写入位置是错误的,而后续写入位置是正确的:

    self.fh.seek(self.rstSnipStartFPtr,0)
    self.fh.write(str);
    sys.stdout.write("writing %s" % str )
    self.rstSnipStartFPtr = self.fh.tell()
Run Code Online (Sandbox Code Playgroud)

我已经读过,由于Python倾向于"提前读取",传递某些read/ readline选项fh会导致错误的判断值.我看到避免这种情况的一个建议是读取整个文件并重写它,这在我的应用程序中不是一个非常有吸引力的解决方案.

如果我将第一个代码段更改为:

  for line in self.fh.read() :
        if ( p.search(line) ):
            self.porSnipStartFPtr = self.fh.tell()
            sys.stdout.write("found regPorSnip")
Run Code Online (Sandbox Code Playgroud)

然后它似乎self.fh.read()只返回字符而不是整行.搜索从不匹配.这似乎也适用于此self.fh.readline().

我的结论是,fh.tell在写操作后查询时只返回有效的文件位置.

有没有办法在阅读/搜索时提取准确的文件位置?

谢谢.

Tim*_*ers 12

原因是(相当模糊地)在文档中解释了文件对象的next()方法:

当文件用作迭代器时,通常在for循环中(例如,对于f:print行中的行),将重复调用next()方法.此方法返回下一个输入行,或在达到EOF时引发StopIteration.为了使for循环成为循环文件行的最有效方式(一种非常常见的操作),next()方法使用隐藏的预读缓冲区.由于使用预读缓冲区,将next()与其他文件方法(如readline())结合使用是行不通的.但是,使用seek()将文件重新定位到绝对位置将刷新预读缓冲区.

返回的值tell()反映了这个隐藏的预读缓冲区已经达到了多远,通常会超出程序实际检索到的字符数千字节.

这方面没有可移植的方法.如果您需要tell()与阅读线混合,请改用文件的readline()方法.权衡是,作为获得可用tell()结果的回报,迭代大文件readline()通常比使用慢得多for line in file_object:.

具体来说,将循环更改为:

line = self.fh.readline()
while line:
    if p.search(line):
        self.porSnipStartFPtr = self.fh.tell()
        sys.stdout.write("found regPorSnip")
    line = fh.readline()
Run Code Online (Sandbox Code Playgroud)

我不确定那是你真正想要的,但是: tell()正在捕捉下一行开头的位置.如果想要行开头的位置,那么你需要改变逻辑,如下所示:

pos = self.fh.tell()
line = self.fh.readline()
while line:
    if p.search(line):
        self.porSnipStartFPtr = pos
        sys.stdout.write("found regPorSnip")
    pos = self.fh.tell()
    line = fh.readline()
Run Code Online (Sandbox Code Playgroud)

或者用"一圈半"来做:

while True:
    pos = self.fh.tell()
    line = self.fh.readline()
    if not line:
        break
    if p.search(line):
        self.porSnipStartFPtr = pos
        sys.stdout.write("found regPorSnip")
Run Code Online (Sandbox Code Playgroud)