2010-04-16 22 views
2

ungetc函數的一些文件中讀取(readlines方法())在Python
功能將文件內容複製到內存(如列表)在Python

我需要處理一個文件,該文件太大,
在內存中拷貝並且因此需要使用文件指針(一次訪問文件一個字節
) - 如在C getc()中一樣。

的附加要求我是
我想將文件指針後退到先前
字節像在C ungetc函數()。

有沒有辦法在Python中做到這一點?

此外,在Python,我可以在
時間的readline讀取一行()

有沒有辦法讀取前一行
倒退?

+12

最糟糕的/詩/永遠。 :) – kennytm 2010-04-16 19:39:53

+0

如何從msvcrt模塊ungetch? – 2010-04-16 20:55:58

回答

3

如果你想直接使用文件指針(我認爲邁克·格雷厄姆的建議是最好不過),您可以使用文件對象的seek()方法,它可以讓你設置內部指針,與read()相結合的方法,它支持指定您想要讀取的字節數的選項參數。

6
  • 您不需要Python沒有或不想要的文件指針。

  • 要經過由線文件中的行,而不必讀取整個事情到內存中,只是遍歷文件對象本身,即

    with open(filename, "r") as f: 
        for line in f: 
         ... 
    

    使用readlines通常是要避免的。

  • 回到行不是超級容易。如果您從不需要返回多條線路,請查看itertools documentation中的pairwise配方。

+0

+1! Python適用於高級思考者。 – 2010-04-16 19:59:13

+0

成對比較seek()的速度如何? – 2010-04-29 03:33:17

+1

@Dragos,我只能猜測,我甚至不想打擾猜測。要知道,我會時間。我只有時間,如果我的代碼運行速度不夠快,我知道*這是我的代碼運行速度太慢的部分。 **瓶頸發生在意想不到的地方**,因此在優化時使用證據至關重要。 http://docs.python.org/library/profile.html可以爲您提供一些工具,幫助您找出代碼花費的時間。有了這些信息,請考慮您是否可以改進算法或嘗試使用http://docs.python.org/library/timeit.html – 2010-04-29 06:12:26

1

編寫類的讀取和緩衝輸入你,並實現ungetc函數就可以了 - 這樣的事情也許(警告:未經檢驗的,寫在編譯時):

class BufRead: 
    def __init__(self,filename): 
     self.filename = filename 
     self.fn = open(filename,'rb') 
     self.buffer = [] 
     self.bufsize = 256 
     self.ready = True 
    def close(self): 
     if self.fn is not None: 
      self.fn.close() 
      self.fn = None 
     self.ready = False 
    def read(self,size=1): 
     l = len(self.buffer) 
     if not self.ready: return None 
     if l <= size: 
      s = self.buffer[:size] 
      self.buffer = self.buffer[size:] 
      return s 
     s = self.buffer 
     size = size - l 
     self.buffer = self.fn.read(min(self.bufsize,size)) 
     if self.buffer is None or len(self.buffer) == 0: 
      self.ready = False 
      return s 
     return s + self.read(size) 
    def ungetc(self,ch): 
     if self.buffer is None: 
      self.buffer = [ch] 
     else: 
      self.buffer.append(ch) 
     self.ready = True 
+0

來計算各種選項。我正在構建一個類,以便對文件進行緩衝讀取 via seek()。 – 2010-04-29 22:58:11

2

確定,這裏就是我想出了用。感謝布倫達建立一堂課的想法。
感謝約什爲理念,用C類的函數求()和read()

#!/bin/python 

# Usage: BufRead.py inputfile 

import sys, os, string 
from inspect import currentframe 

# Debug function usage 
# 
# if DEBUG: 
# debugLogMsg(currentframe().f_lineno,currentframe().f_code.co_filename) 
# print ... 
def debugLogMsg(line,file,msg=""): 
    print "%s:%s %s" % (file,line,msg) 

# Set DEBUG off. 
DEBUG = 0 

class BufRead: 
    def __init__(self,filename): 
    self.__filename   = filename 
    self.__file    = open(self.__filename,'rb') 
    self.__fileposition  = self.__file.tell() 
    self.__file.seek(0, os.SEEK_END) 
    self.__filesize   = self.__file.tell() 
    self.__file.seek(self.__fileposition, os.SEEK_SET) 

    def close(self): 
    if self.__file is not None: 
     self.__file.close() 
     self.__file = None 

    def seekstart(self): 
    if self.__file == None: 
     self.__file.seek(0, os.SEEK_SET) 
     self.__fileposition = self.__file.tell() 

    def seekend(self): 
    if self.__file == None: 
     self.__file.seek(-1, os.SEEK_END) 
     self.__fileposition = self.__file.tell() 

    def getc(self): 
    if self.__file == None: 
     return None 
    self.__fileposition = self.__file.tell() 
    if self.__fileposition < self.__filesize: 
     byte = self.__file.read(1) 
     self.__fileposition = self.__file.tell() 
     return byte 
    else: 
     return None 

    def ungetc(self): 
    if self.__file == None: 
     return None 
    self.__fileposition = self.__file.tell() 
    if self.__fileposition > 0: 
     self.__fileposition = self.__fileposition - 1 
     self.__file.seek(self.__fileposition, os.SEEK_SET) 
     byte = self.__file.read(1) 
     self.__file.seek(self.__fileposition, os.SEEK_SET) 
     return byte 
    else: 
     return None 

    # uses getc() and ungetc() 
    def getline(self): 
    if self.__file == None: 
     return None 
    self.__fileposition = self.__file.tell() 

    if self.__fileposition < self.__filesize: 
     startOfLine = False 
     line = "" 

     while True: 
     if self.__fileposition == 0: 
      startOfLine = True 
      break 
     else: 
      c = self.ungetc() 
      if c == '\n': 
      c = self.getc() 
      startOfLine = True 
      break 

     if startOfLine: 
     c = self.getc() 
     if c == '\n': 
      return '\n' 
     else: 
      self.ungetc() 

     while True: 
      c = self.getc() 
      if c == '\n': 
      line += c 
      c = self.getc() 
      if c == None: 
       return line 
      if c == '\n': 
       self.ungetc() 
      return line 
      elif c == None: 
      return line 
      else: 
      line += c 
    else: 
     return None 

    # uses getc() and ungetc() 
    def ungetline(self): 
    if self.__file == None: 
     return None 
    self.__fileposition = self.__file.tell() 

    if self.__fileposition > 0: 
     endOfLine = False 
     line = "" 

     while True: 
     if self.__fileposition == self.__filesize: 
      endOfLine = True 
      break 
     else: 
      c = self.getc() 
      if c == '\n': 
      c = self.ungetc() 
      endOfLine = True 
      break 

     if endOfLine: 
     c = self.ungetc() 
     if c == '\n': 
      return '\n' 
     else: 
      self.getc() 

     while True: 
      c = self.ungetc() 
      if c == None: 
      return line 
      if c == '\n': 
      line += c 
      c = self.ungetc() 
      if c == None: 
       return line 
      if c == '\n': 
       self.getc() 
      return line 
      elif c == None: 
      return line 
      else: 
      line = c + line 
    else: 
     return None 

def main(): 
    if len(sys.argv) == 2: 
    print sys.argv[1] 
    b = BufRead(sys.argv[1]) 

    sys.stdout.write(
     '----------------------------------\n' \ 
     '- TESTING GETC     \n' \ 
     '----------------------------------\n') 

    while True: 
     c = b.getc() 
     if c == None: 
     sys.stdout.write('\n') 
     break 
     else: 
     sys.stdout.write(c) 

    sys.stdout.write(
     '----------------------------------\n' \ 
     '- TESTING UNGETC     \n' \ 
     '----------------------------------\n') 

    while True: 
     c = b.ungetc() 
     if c == None: 
     sys.stdout.write('\n') 
     break 
     else: 
     sys.stdout.write(c) 

    sys.stdout.write(
     '----------------------------------\n' \ 
     '- TESTING GETLINE     \n' \ 
     '----------------------------------\n') 

    b.seekstart() 

    while True: 
     line = b.getline() 
     if line == None: 
     sys.stdout.write('\n') 
     break 
     else: 
     sys.stdout.write(line) 

    sys.stdout.write(
     '----------------------------------\n' \ 
     '- TESTING UNGETLINE    \n' \ 
     '----------------------------------\n') 

    b.seekend() 

    while True: 
     line = b.ungetline() 
     if line == None: 
     sys.stdout.write('\n') 
     break 
     else: 
     sys.stdout.write(line) 

    b.close() 

if __name__=="__main__": main() 
0

我不想做數十億緩衝單個字符文件的讀取加上我就想辦法
調試文件指針的位置。因此,我解決了除char或line之外的文件位置
以及使用mmap將文件映射到內存的問題。 (並讓mmap
處理分頁)我認爲如果文件真的非常大,這會有點問題。 (如大於物理內存的數量)那麼當mmap開始進入 虛擬內存和事情可能會變得非常慢。目前,它在大約4分鐘內處理一個50 MB的文件。

import sys, os, string, re, time 
from mmap import mmap 

class StreamReaderDb: 
    def __init__(self,stream): 
    self.__stream    = mmap(stream.fileno(), os.path.getsize(stream.name)) 
    self.__streamPosition  = self.__stream.tell() 
    self.__stream.seek(0     , os.SEEK_END) 
    self.__streamSize   = self.__stream.tell() 
    self.__stream.seek(self.__streamPosition, os.SEEK_SET) 

    def setStreamPositionDb(self,streamPosition): 
    if self.__stream == None: 
     return None 
    self.__streamPosition = streamPosition 
    self.__stream.seek(self.__streamPosition, os.SEEK_SET) 

    def streamPositionDb(self): 
    if self.__stream == None: 
     return None 
    return self.__streamPosition 

    def streamSize(self): 
    if self.__stream == None: 
     return None 
    return self.__streamSize 

    def close(self): 
    if self.__stream is not None: 
     self.__stream.close() 
     self.__stream = None 

    def seekStart(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(0) 

    def seekEnd(self): 
    if self.__stream == None: 
     return None 
    self.__stream.seek(-1, os.SEEK_END) 
    self.setStreamPositionDb(self.__stream.tell()) 

    def getcDb(self): 
    if self.__stream == None: 
     return None,None 
    self.setStreamPositionDb(self.__stream.tell()) 
    if self.streamPositionDb() < self.streamSize(): 
     byte = self.__stream.read(1) 
     self.setStreamPositionDb(self.__stream.tell()) 
     return byte,self.streamPositionDb() 
    else: 
     return None,self.streamPositionDb() 

    def unGetcDb(self): 
    if self.__stream == None: 
     return None,None 
    self.setStreamPositionDb(self.__stream.tell()) 
    if self.streamPositionDb() > 0: 
     self.setStreamPositionDb(self.streamPositionDb() - 1) 
     byte = self.__stream.read(1) 
     self.__stream.seek(self.streamPositionDb(), os.SEEK_SET) 
     return byte,self.streamPositionDb() 
    else: 
     return None,self.streamPositionDb() 

    def seekLineStartDb(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(self.__stream.tell()) 

    if self.streamPositionDb() < self.streamSize(): 
     # Back up to the start of the line 
     while True: 
     if self.streamPositionDb() == 0: 
      return self.streamPositionDb() 
     else: 
      c,fp = self.unGetcDb() 
      if c == '\n': 
      c,fp = self.getcDb() 
      return fp 
    else: 
     return None 

    def seekPrevLineEndDb(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(self.__stream.tell()) 

    if self.streamPositionDb() < self.streamSize(): 
     # Back up to the start of the line 
     while True: 
     if self.streamPositionDb() == 0: 
      return self.streamPositionDb() 
     else: 
      c,fp = self.unGetcDb() 
      if c == '\n': 
      return fp 
    else: 
     return None 

    def seekPrevLineStartDb(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(self.__stream.tell()) 

    if self.streamPositionDb() < self.streamSize(): 
     # Back up to the start of the line 
     while True: 
     if self.streamPositionDb() == 0: 
      return self.streamPositionDb() 
     else: 
      c,fp = self.unGetcDb() 
      if c == '\n': 
      return self.seekLineStartDb() 
    else: 
     return None 

    def seekLineEndDb(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(self.__stream.tell()) 

    if self.streamPositionDb() > 0: 
     while True: 
     if self.streamPositionDb() == self.streamSize(): 
      return self.streamPositionDb() 
     else: 
      c,fp = self.getcDb() 
      if c == '\n': 
      c,fp = self.unGetcDb() 
      return fp 
    else: 
     return None 

    def seekNextLineEndDb(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(self.__stream.tell()) 

    if self.streamPositionDb() > 0: 
     while True: 
     if self.streamPositionDb() == self.streamSize(): 
      return self.streamPositionDb() 
     else: 
      c,fp = self.getcDb() 
      if c == '\n': 
      return fp 
    else: 
     return None 

    def seekNextLineStartDb(self): 
    if self.__stream == None: 
     return None 
    self.setStreamPositionDb(self.__stream.tell()) 

    if self.streamPositionDb() > 0: 
     while True: 
     if self.streamPositionDb() == self.streamSize(): 
      return self.streamPositionDb() 
     else: 
      c,fp = self.getcDb() 
      if c == '\n': 
      return self.seekLineStartDb() 
    else: 
     return None 

    # uses getc() and ungetc() 
    def getLineDb(self): 
    if self.__stream == None: 
     return None,None 
    self.setStreamPositionDb(self.__stream.tell()) 

    line = "" 

    if self.seekLineStartDb() != None: 
     c,fp = self.getcDb() 
     if c == '\n': 
     return c,self.streamPositionDb() 
     else: 
     self.unGetcDb() 

     while True: 
     c,fp = self.getcDb() 
     if c == '\n': 
      line += c 
      c,fp = self.getcDb() 
      if c == None: 
      return line,self.streamPositionDb() 
      self.unGetcDb() 
      return line,self.streamPositionDb() 
     elif c == None: 
      return line,self.streamPositionDb() 
     else: 
      line += c 
    else: 
     return None,self.streamPositionDb() 

    # uses getc() and ungetc() 
    def unGetLineDb(self): 
    if self.__stream == None: 
     return None,None 
    self.setStreamPositionDb(self.__stream.tell()) 

    line = "" 

    if self.seekLineEndDb() != None: 
     c,fp = self.unGetcDb() 
     if c == '\n': 
     return c,self.streamPositionDb() 
     else: 
     self.getcDb() 

     while True: 
     c,fp = self.unGetcDb() 
     if c == None: 
      return line,self.streamPositionDb() 
     if c == '\n': 
      line += c 
      c,fp = self.unGetcDb() 
      if c == None: 
      return line,self.streamPositionDb() 
      self.getcDb() 
      return line,self.streamPositionDb() 
     elif c == None: 
      return line,self.streamPositionDb() 
     else: 
      line = c + line 
    else: 
     return None,self.streamPositionDb() 
0

這個問題最初是因爲我需要建立一個詞法分析器。
GETC()和ungetc函數()是在第一(以獲得讀取錯誤的出路和
打造的狀態機)狀態機完成後,
GETC()和ungetc函數()有用變成負擔因爲它們需要很長時間直接從存儲中讀取 。

當狀態機完成(調試任何IO問題,
終結狀態),我優化了詞法分析器。

將塊文件(或頁面)中的源文件讀入內存並運行
每個頁面上的狀態機會產生最佳時間結果。

我發現如果不使用getc()和ungetc(),則可以節省相當多的時間直接從文件讀取