2011-06-16 57 views
6

在Python 2.5,我讀一個結構化文本數據文件(大小〜30 MB)用文件指針:有沒有一種簡單的方法來確定文件指針在哪一行上?

fp = open('myfile.txt', 'r') 
line = fp.readline() 
# ... many other fp.readline() processing steps, which 
# are used in different contexts to read the structures 

但隨後,在解析文件,我打了一些有趣的事情,我想報告行號,所以我可以在文本編輯器中調查該文件。我可以使用fp.tell()來告訴我字節偏移量在哪裏(例如16548974L),但是沒有「fp.tell_line_number()」來幫助我將其轉換爲行號。

是否有一個Python內置或擴展來輕鬆跟蹤和「告訴」文本文件指針所在的行號?

注:我not asking使用line_number += 1風格櫃檯,我呼籲在不同環境下fp.readline()和方法需要更多的調試比它的價值插入計數器代碼的右下角。

回答

13

這個問題的一個典型解決方案是定義一個新類,它包裝了一個file的現有實例,該實例自動計算數字。像這樣的東西(就在我的頭頂,我沒有測試過這一點):

class FileLineWrapper(object): 
    def __init__(self, f): 
     self.f = f 
     self.line = 0 
    def close(self): 
     return self.f.close() 
    def readline(self): 
     self.line += 1 
     return self.f.readline() 
    # to allow using in 'with' statements 
    def __enter__(self): 
     return self 
    def __exit__(self, exc_type, exc_val, exc_tb): 
     self.close() 

使用方法如下:

f = FileLineWrapper(open("myfile.txt", "r")) 
f.readline() 
print(f.line) 

它看起來像標準模塊fileinput確實大同小異東西(以及其他一些東西);如果你喜歡,你可以使用它。

+0

+1,好簡單的解決方案,因爲它不僅需要'open'呼籲改變。您可能想爲其他任何使用的函數提供包裝(例如'close'),但它們應該是相當小的pass-thru函數。 – paxdiablo 2011-06-16 04:32:49

+0

哦,對,'close'很方便,我會補充一點。 – 2011-06-16 04:34:30

+0

這兩種解決方案都很棒,太棒了! – 2011-06-16 04:36:50

1

我不這麼認爲,不是你想要的方式(如open返回的Python文件句柄的標準內置功能)。

如果您不習慣於在閱讀線條或使用包裝類時手動跟蹤行號(順便說一下,GregH和senderle提供的出色建議),那麼我認爲您只需簡單地使用fp.tell()圖,並返回到文件的開頭,閱讀,直到你到達那裏。

這不是糟糕的一個選擇,因爲我假設錯誤條件比任何游泳工作的可能性都小。如果一切正常,沒有影響。

如果出現錯誤,那麼您有重新掃描文件的額外工作。如果文件是大,可能會影響您的感知表現 - 如果這是一個問題,您應該考慮到這一點。

10

您可能會發現fileinput模塊很有用。它爲迭代任意數量的文件提供了一個通用接口。從文檔相關的一些亮點:

fileinput.lineno()

返回行的累計行號剛讀。在第一行讀取之前,返回0.在讀取最後一個文件的最後一行之後,返回該行的行號。

fileinput.filelineno()

返回當前文件的行號。在讀取第一行之前,返回0.在讀取最後一個文件的最後一行之後,返回文件中該行的行號。

+0

小警告:'fileinput'似乎不支持'with '在Python2.7中的語句... – Julien 2016-12-07 01:23:00

0

一種方式是遍歷線,並保持已經看到的行數的明確計數:給予該

>>> f=open('text.txt','r') 
>>> from itertools import izip 
>>> from itertools import count 
>>> f=open('test.java','r') 
>>> for line_no,line in izip(count(),f): 
...  print line_no,line 
0

下面的代碼創建了一個功能Which_Line_for_Position(POS)號位置POS的,即所述號線的在位於位於位置POS 字符在文件中。

該函數可以與任何位置一起作爲參數使用,與函數調用之前文件指針的當前位置的值和該指針運動的歷史無關。

因此,使用此函數,不僅限於在線路上不間斷迭代期間確定當前線路的數量,這與Greg Hewgill的解決方案一樣。

with open(filepath,'rb') as f: 
    GIVE_NO_FOR_END = {} 
    end = 0 
    for i,line in enumerate(f): 
     end += len(line) 
     GIVE_NO_FOR_END[end] = i 
    if line[-1]=='\n': 
     GIVE_NO_FOR_END[end+1] = i+1 
    end_positions = GIVE_NO_FOR_END.keys() 
    end_positions.sort() 

def Which_Line_for_Position(pos, 
          dic = GIVE_NO_FOR_END, 
          keys = end_positions, 
          kmax = end_positions[-1]): 
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None 

同樣的解決方案可以與模塊的幫助下寫成的FileInput

import fileinput 

GIVE_NO_FOR_END = {} 
end = 0 
for line in fileinput.input(filepath,'rb'): 
    end += len(line) 
    GIVE_NO_FOR_END[end] = fileinput.filelineno() 
if line[-1]=='\n': 
    GIVE_NO_FOR_END[end+1] = fileinput.filelineno()+1 
fileinput.close() 

end_positions = GIVE_NO_FOR_END.keys() 
end_positions.sort() 

def Which_Line_for_Position(pos, 
          dic = GIVE_NO_FOR_END, 
          keys = end_positions, 
          kmax = end_positions[-1]): 
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None 

但這種解決方案有一些不便之處:

  • 它需要導入模塊的FileInput
  • 它刪除文件的所有內容!我的代碼中一定有錯,但我不知道文件輸入足以找到它。或者它是fileinput.input()函數的正常行爲?
  • 似乎該文件是在任何迭代啓動之前首先完全讀取的。如果是這樣,對於非常大的文件,文件的大小可能會超過RAM的容量。我不確定這一點:我試圖用1.5 GB的文件進行測試,但這個時間很長,我暫時放棄了這一點。如果這一點是正確的,則構成使用另一種解決方案的論據枚舉()

爲例:

text = '''Harold Acton (1904–1994) 
Gilbert Adair (born 1944) 
Helen Adam (1909–1993) 
Arthur Henry Adams (1872–1936) 
Robert Adamson (1852–1902) 
Fleur Adcock (born 1934) 
Joseph Addison (1672–1719) 
Mark Akenside (1721–1770) 
James Alexander Allan (1889–1956) 
Leslie Holdsworthy Allen (1879–1964) 
William Allingham (1824/28-1889) 
Kingsley Amis (1922–1995) 
Ethel Anderson (1883–1958) 
Bruce Andrews (born 1948) 
Maya Angelou (born 1928) 
Rae Armantrout (born 1947) 
Simon Armitage (born 1963) 
Matthew Arnold (1822–1888) 
John Ashbery (born 1927) 
Thomas Ashe (1836–1889) 
Thea Astley (1925–2004) 
Edwin Atherstone (1788–1872)''' 


#with open('alao.txt','rb') as f: 

f = text.splitlines(True) 
# argument True in splitlines() makes the newlines kept 

GIVE_NO_FOR_END = {} 
end = 0 
for i,line in enumerate(f): 
    end += len(line) 
    GIVE_NO_FOR_END[end] = i 
if line[-1]=='\n': 
    GIVE_NO_FOR_END[end+1] = i+1 
end_positions = GIVE_NO_FOR_END.keys() 
end_positions.sort() 


print '\n'.join('line %-3s ending at position %s' % (str(GIVE_NO_FOR_END[end]),str(end)) 
       for end in end_positions) 

def Which_Line_for_Position(pos, 
          dic = GIVE_NO_FOR_END, 
          keys = end_positions, 
          kmax = end_positions[-1]): 
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None 

print 
for x in (2,450,320,104,105,599,600): 
    print 'pos=%-6s line %s' % (x,Which_Line_for_Position(x)) 

結果

line 0 ending at position 25 
line 1 ending at position 51 
line 2 ending at position 74 
line 3 ending at position 105 
line 4 ending at position 132 
line 5 ending at position 157 
line 6 ending at position 184 
line 7 ending at position 210 
line 8 ending at position 244 
line 9 ending at position 281 
line 10 ending at position 314 
line 11 ending at position 340 
line 12 ending at position 367 
line 13 ending at position 393 
line 14 ending at position 418 
line 15 ending at position 445 
line 16 ending at position 472 
line 17 ending at position 499 
line 18 ending at position 524 
line 19 ending at position 548 
line 20 ending at position 572 
line 21 ending at position 600 

pos=2  line 0 
pos=450  line 16 
pos=320  line 11 
pos=104  line 3 
pos=105  line 4 
pos=599  line 21 
pos=600  line None 

然後,將具有功能Which_Line_for_Position(),很容易獲得當前行號:只是路過f.tell()作爲參數傳遞給功能

但是警告:當使用f.tell(),做文件中的文件的指針的運動,這是絕對必要的文件是以二進制模式打開:「RB」「RB +」「AB」或...

-1

關於solution by @eyquem,我建議使用mode='r'與文件輸入模塊和fileinput.lineno()選項,它已爲我工作。

這裏是我如何在我的代碼中實現這些選項。

table=fileinput.input('largefile.txt',mode="r") 
    if fileinput.lineno() >= stop : # you can disregard the IF condition but I am posting to illustrate the approach from my code. 
      temp_out.close() 
+1

這並沒有提供問題的答案。要批評或要求作者澄清,在他們的帖子下留下評論 - 你總是可以評論你自己的帖子,一旦你有足夠的[聲譽](http://stackoverflow.com/help/whats-reputation),你會能夠[評論任何帖子](http://stackoverflow.com/help/privileges/comment)。 - [評論] [發表評論](/ review/low-quality-posts/10786418) – Prune 2016-01-05 19:55:39

+0

@ Prune - 感謝您的評論,並且我在學習中加入了一段代碼,以增加我的建議清晰度。 – speedchase 2016-01-05 21:36:39

+0

請注意,「以上」在答案中沒有上下文。答案投票更改和答案可以以多種不同的方式進行排序。更好地鏈接到你所指的答案。 – ale 2016-01-06 00:43:54

7

下面的代碼將打印的行號(其中,該指針是當前上),同時通過文件( 'testfile的')遍歷

file=open("testfile", "r") 
for line_no, line in enumerate(file): 
    print line_no  # The content of the line is in variable 'line' 
file.close() 

輸出:

1 
2 
3 
... 
+0

請隨時向您的代碼添加一些解釋... – andreas 2016-09-25 20:01:49

+0

感謝您的建議。我會 – 2016-09-26 11:24:37

0

最近遇到了類似的問題,並提出了這個基於類的解決方案。

class TextFileProcessor(object): 

    def __init__(self, path_to_file): 
     self.print_line_mod_number = 0 
     self.__path_to_file = path_to_file 
     self.__line_number = 0 

    def __printLineNumberMod(self): 
     if self.print_line_mod_number != 0: 
      if self.__line_number % self.print_line_mod_number == 0: 
       print(self.__line_number) 

    def processFile(self): 
     with open(self.__path_to_file, 'r', encoding='utf-8') as text_file: 
      for self.__line_number, line in enumerate(text_file, start=1): 
       self.__printLineNumberMod() 

       # do some stuff with line here. 

設置print_line_mod_number屬性爲您要記錄,然後調用processFile節奏。

例如...如果你想每100行的反饋,它會看起來像這樣。

tfp = TextFileProcessor('C:\\myfile.txt') 
tfp.print_line_mod_number = 100 
tfp.processFile() 

控制檯輸出將

100 
200 
300 
400 
etc... 
相關問題