2010-04-16 16 views
2

Certains fichiers lus fonctions (readlines()) en Python
copier le contenu du fichier à la mémoire (comme une liste)ungetc en Python

je dois traiter un fichier qui est trop grand pour
être copié dans la mémoire et en tant que tel besoin d'utiliser
un pointeur de fichier (pour accéder au fichier un octet
à la fois) - comme dans C getc().

L'exigence supplémentaire est que je
j'aimerais revenir en arrière le pointeur de fichier précédent
octets comme en C ungetc().

Existe-t-il un moyen de le faire en Python?

En outre, en Python, je peux lire une ligne à
temps avec readline()

Est-il possible de lire la ligne précédente
aller en arrière?

+12

pire/poème/jamais. :) – kennytm

+0

Qu'en est-il de ungetch depuis le module msvcrt? –

Répondre

3

Si vous souhaitez utiliser un pointeur de fichier directement (je pense que la suggestion de Mike Graham est mieux que), vous pouvez utiliser seek() de l'objet fichier méthode qui vous permet de définir le pointeur interne, combiné avec la méthode read(), qui supporte un argument d'option spécifiant le nombre d'octets que vous souhaitez lire.

6
  • Vous n'avez pas besoin de pointeurs de fichiers, ce que Python n'a pas ou ne veut pas.

  • passer par une ligne de fichiers en ligne sans lire tout cela en mémoire, juste itérer sur l'objet de fichier lui-même, à savoir

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

    est généralement à l'aide readlines à éviter. Le retour d'une ligne n'est pas quelque chose que vous pouvez faire très facilement. Si vous n'avez jamais besoin de retourner plus d'une ligne, consultez la recette pairwise dans le itertools documentation.

+0

+1! Python est pour les penseurs de haut niveau. –

+0

Comment comparer par paire en vitesse avec seek()? –

+1

@Dragos, je ne pouvais que deviner, et je ne voudrais même pas prendre la peine de deviner. Pour savoir, je passerais du temps. Et je ne ferais que du temps si j'avais du code qui ne fonctionnait pas assez vite et je * savais * que c'était la partie de mon code qui fonctionnait trop lentement. ** Les goulets d'étranglement se produisent à des endroits inattendus **, il est donc essentiel d'utiliser des preuves lors de l'optimisation. http://docs.python.org/library/profile.html peut vous donner quelques outils qui vous aideront à découvrir où votre code passe son temps. Avec cette information, examinez si vous pouvez améliorer votre algorithme ou essayez de chronométrer diverses options avec http://docs.python.org/library/timeit.html –

1

Écrivez une classe les lectures et les tampons d'entrée pour vous, et mettre en œuvre ungetc sur elle - quelque chose comme ça peut-être (avertissement: non testé, écrit lors de la compilation):

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

Je suis en train de construire une classe pour faire des lectures tamponnées d'un fichier via seek(). –

2

OK, voici ce que je suis venu avec. Merci à Brenda pour l'idée de construire une classe.
Merci Josh pour l'idée d'utiliser C comme des fonctions seek() et lire()

#!/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

Je ne veux pas faire des milliards de fichier unique char unbuffered lit plus je voulais un moyen
pour déboguer le position du pointeur de fichier. Par conséquent, j'ai résolu de renvoyer la position
en plus d'un caractère ou d'une ligne et d'utiliser mmap pour mapper le fichier en mémoire. (et laissez mmap
gérer pagination) Je pense que ce sera un peu un problème si le fichier est vraiment, vraiment gros. (comme en plus grand que la quantité de mémoire physique) C'est alors que mmap commencerait à entrer dans la mémoire virtuelle et les choses pourraient devenir très lentes. Pour l'instant, il traite un fichier de 50 Mo en environ 4 min.

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

La question a été initialement motivée par mon besoin de construire un analyseur lexical.
getc() et ungetc() sont utiles dans un premier temps (pour obtenir les lectures bugs le chemin et
pour construire la machine d'état) Après la machine d'état se fait,
getc() et ungetc() devenir un passif car ils prennent trop de temps à lire directement à partir du stockage.

Lorsque la machine d'état a été terminée (débogué tous les problèmes d'E/S,
a finalisé les états), j'ai optimisé l'analyseur lexical.

En lisant le fichier source en morceaux (ou pages) en mémoire et en exécutant
la machine d'état sur chaque page donne le meilleur résultat de temps.

J'ai trouvé qu'un temps considérable est enregistré si getc() et ungetc() ne sont pas utilisés
pour lire directement à partir du fichier.