2012-07-03 8 views
7

Tengo datos almacenados en una colección de archivos o en un solo archivo compuesto. El archivo compuesto se forma al concatenar todos los archivos separados, y luego preceder a todo con un encabezado que proporciona los desplazamientos y tamaños de las partes constituyentes. Me gustaría tener un objeto similar a un archivo que presente una vista del archivo compuesto, donde la vista representa solo uno de los archivos miembros. (De esta forma, puedo tener funciones para leer los datos que aceptan un objeto de archivo real o un objeto de "vista", y no necesitan preocuparse por cómo se almacena ningún conjunto de datos en particular). ¿Qué biblioteca hará esto por mí?¿Cómo puedo tratar una sección de un archivo como si fuera un archivo en sí?

La clase mmap parecía prometedora ya que está construida a partir de un archivo, una longitud y un desplazamiento, que es exactamente lo que tengo, pero el desplazamiento debe alinearse con la granularidad de asignación del sistema de archivos subyacente, y los archivos I ' m leer no cumple ese requisito. El nombre de la clase MultiFile se ajusta a la ley, pero está diseñado para archivos adjuntos en mensajes de correo electrónico, y mis archivos no tienen esa estructura.

Las operaciones de archivos que más me interesan son read, seek y tell. Los archivos que estoy leyendo son binarios, por lo que las funciones orientadas a texto como readline y next no son tan cruciales. Eventualmente también podría necesitar write, pero estoy dispuesto a renunciar a esa función por ahora, ya que no estoy seguro de cómo debe comportarse.

+1

¿Puedes simplemente envolver un objeto de archivo en una clase de conveniencia que tenga los métodos 'read',' seek' y 'tell' que calculan la posición real del archivo desde la pseudo-posición? – mgilson

+0

Además, ¿qué tan grandes son los archivos? ¿Son lo suficientemente pequeños como para caber cómodamente en la memoria? Si ese es el caso, puede dividirlos usando 'StringIO' – mgilson

+0

_" [..] pero el desplazamiento debe estar alineado con la granularidad de asignación del sistema de archivos subyacente, y los archivos que estoy leyendo no cumplen ese requisito. "_ ... ¿puedes aclarar esto? –

Respuesta

4

sé que estaban en busca de una biblioteca, pero tan pronto como leí esta pregunta que pensé que me gustaría escribir mi propia. Así que aquí está:

import os 

class View: 
    def __init__(self, f, offset, length): 
     self.f = f 
     self.f_offset = offset 
     self.offset = 0 
     self.length = length 

    def seek(self, offset, whence=0): 
     if whence == os.SEEK_SET: 
      self.offset = offset 
     elif whence == os.SEEK_CUR: 
      self.offset += offset 
     elif whence == os.SEEK_END: 
      self.offset = self.length+offset 
     else: 
      # Other values of whence should raise an IOError 
      return self.f.seek(offset, whence) 
     return self.f.seek(self.offset+self.f_offset, os.SEEK_SET) 

    def tell(self): 
     return self.offset 

    def read(self, size=-1): 
     self.seek(self.offset) 
     if size<0: 
      size = self.length-self.offset 
     size = max(0, min(size, self.length-self.offset)) 
     self.offset += size 
     return self.f.read(size) 

if __name__ == "__main__": 
    f = open('test.txt', 'r') 

    views = [] 
    offsets = [i*11 for i in range(10)] 

    for o in offsets: 
     f.seek(o+1) 
     length = int(f.read(1)) 
     views.append(View(f, o+2, length)) 

    f.seek(0) 

    completes = {} 
    for v in views: 
     completes[v.f_offset] = v.read() 
     v.seek(0) 

    import collections 
    strs = collections.defaultdict(str) 
    for i in range(3): 
     for v in views: 
      strs[v.f_offset] += v.read(3) 
    strs = dict(strs) # We want it to raise KeyErrors after that. 

    for offset, s in completes.iteritems(): 
     print offset, strs[offset], completes[offset] 
     assert strs[offset] == completes[offset], "Something went wrong!" 

Y escribió otro guión para generar el archivo "test.txt":

import string, random 

f = open('test.txt', 'w') 

for i in range(10): 
    rand_list = list(string.ascii_letters) 
    random.shuffle(rand_list) 
    rand_str = "".join(rand_list[:9]) 
    f.write(".%d%s" % (len(rand_str), rand_str)) 

Se trabajó para mí. Los archivos en los que probé no son archivos binarios como los tuyos, y no son tan grandes como los tuyos, pero esto podría ser útil, espero. Si no, entonces gracias, ese fue un buen desafío: D

Además, me preguntaba si estos son en realidad varios archivos, ¿por qué no utilizar algún tipo de formato de archivo y usar sus bibliotecas para leerlos?

Espero que ayude.

+0

Gracias. Esto fue útil. Puede ser agradable utilizar un formato de archivo compuesto mejor definido, pero nuestro producto ha estado produciendo archivos como este durante casi una década, por lo que es demasiado tarde para cambiar ahora. Tengo que escribir el código para manejar lo que los archivos * son *, no por lo que * deseo * que eran. –

3

Dependiendo de lo complicado que sea necesario, algo como esto debería funcionar: he dejado algunos detalles, ya que no sé qué tan cerca necesita emular un objeto de archivo (por ej. utilizar nunca obj.read(), o va a utilizar siempre obj.read(nbytes)):

class FileView(object): 
    def __init__(self,file,offset,length): 
     self._file=file 
     self._offset=offset 
     self._length=length 

    def seek(self,pos): 
     #May need to get a little fancier here to support the second argument to seek. 
     return self._file.seek(self._offset+pos) 

    def tell(self): 
     return self._file.tell()-self._offset 

    def read(self,*args): 
     #May need to get a little more complicated here to make sure that the number of 
     #bytes read is smaller than the number of bytes available for this file 
     return self._file.read(*args) 
Cuestiones relacionadas