2017-09-25 23 views
2

如何在Python3中遍歷文件時找到文件指針的位置?在Python3中迭代遍歷文件的行時使用「tell()」的替代方法?

在Python 2.7中它很平凡,使用tell()。在Python3相同的調用拋出OSError

Traceback (most recent call last): 
    File "foo.py", line 113, in check_file 
    pos = infile.tell() 
OSError: telling position disabled by next() call 

我的用例正在一個進度條讀取大的CSV文件。計算總計行數太貴,需要額外的通行證。近似值非常有用,我不關心緩衝區或其他噪聲源,我想知道它是否需要10秒或10分鐘。

重現問題的簡單代碼。它的工作原理上的Python 2.7的預期,但引發關於Python 3:

file_size = os.stat(path).st_size 
with open(path, "r") as infile: 
    reader = csv.reader(infile) 
    for row in reader: 
     pos = infile.tell() # OSError: telling position disabled by next() call 
     print("At byte {} of {}".format(pos, file_size)) 

這個答案https://stackoverflow.com/a/29641787/321772表明,問題是next()方法迭代過程中禁用tell()。替代方法是逐行手動讀取,但該代碼位於CSV模塊內部,因此我不能理解它。我也無法通過禁用tell()來了解Python 3的優點。

那麼在Python 3中遍歷文件的行時,找出字節偏移量的首選方法是什麼?

+0

你可以使用'枚舉'並返回行號。就像那樣,你可以給用戶一些有用的東西,而不必遍歷文件兩次 –

+0

@MaartenFabré當然,打印行號是很有用的,如果只是爲了顯示腳本沒有被卡住,並且它也是你所能做的不知道長度(即從標準輸入讀數)。但是,「完成55%,剩餘2分鐘」比「讀取10,543,000行」要好得多。 – Adam

回答

4

。 csv模塊只是希望reader調用的第一個參數是一個迭代器,它在每個next調用中返回一行,因此您可以使用迭代器包裝器來計算字符數,如果您希望計數值準確,以二進制模式打開文件,但事實上,這很好,因爲您將沒有csv模塊預期的行結束轉換。

因此,一個可能的包裝是:

class SizedReader: 
    def __init__(self, fd, encoding='utf-8'): 
     self.fd = fd 
     self.size = 0 
     self.encoding = encoding # specify encoding in constructor, with utf8 as default 
    def __next__(self): 
     line = next(self.fd) 
     self.size += len(line) 
     return line.decode(self.encoding) # returns a decoded line (a true Python 3 string) 
    def __iter__(self): 
     return self 
然後

您的代碼將成爲:

file_size = os.stat(path).st_size 
with open(path, "rb") as infile: 
    szrdr = SizedReader(infile) 
    reader = csv.reader(szrdr) 
    for row in reader: 
     pos = szrdr.size # gives position at end of current line 
     print("At byte {} of {}".format(pos, file_size)) 

這裏的好消息是,你保持csv模塊的所有的力量,包括換行符報價字段...

+0

這有效。雖然你不需要擔心編碼,只要拿出你得到的東西,找到它的長度,然後歸還它。這樣你就不會改變解碼行爲。還要注意,你需要一個'def next(self):return self .__ next __()',所以相同的代碼在Python 2和3上都可以工作。 – Adam

+0

@Adam:這個問題特別關於Python 3。如果你不解碼在二進制模式下讀取的內容,你將得到字節而不是字符串。 Python2和Python3中csv模塊的表現完全不同,這就是爲什麼我沒有嘗試給出兼容代碼的原因。這確實是可能的,但會更復雜。 –

+0

是的,但問題並未以二進制模式打開文件。 – Adam

0

如果您沒有特別的csv模塊感覺舒適。你可以這樣做:

import os, csv 

file_size = os.path.getsize('SampleCSV.csv') 
pos = 0 

with open('SampleCSV.csv', "r") as infile: 
    for line in infile: 
     pos += len(line) + 1 # 1 for newline character 
     row = line.rstrip().split(',') 
     print("At byte {} of {}".format(pos, file_size)) 

但是這可能不是在字段本身包含\情況下工作」

例:1,"Hey, you..",22:04雖然這些也可以採取使用正則表達式的護理