2011-03-17 12 views
9

Estoy cargando un archivo csv/tsv desde un formulario en GAE, y trato de analizar el archivo con el módulo python csv.Cargar y analizar el archivo csv con "nueva línea universal" en python en Google App Engine

Como describir here, los archivos cargados en GAE son cadenas.
Así que trato a mis subido cadena de un objeto de tipo fichero:

file = self.request.get('catalog') 
catalog = csv.reader(StringIO.StringIO(file),dialect=csv.excel_tab) 

Pero nuevas líneas en mis archivos no son necesariamente '\ n' (gracias a sobresalir ..), y se genera un error:
Error: carácter de nueva línea visto en el campo sin comillas: ¿necesita abrir el archivo en el modo universal-nueva línea?

¿Alguien sabe cómo usar StringIO.StringIO para tratar cadenas como archivos abiertos en universal-nueva línea?

+0

De acuerdo con la documentación de Python, el modo por defecto de StringIO es nueva línea universal. Algo extraño puede estar sucediendo en su archivo de datos. – Calvin

+0

@ Calvin _ "De acuerdo con los documentos de Python, el modo predeterminado de StringIO es la nueva línea universal" _ ¿No encontré dónde los documentos dicen que podría mostrarlo, por favor? – eyquem

+0

@eyquem Han pasado 2.5 años, por lo que los documentos pueden haber cambiado, pero http://docs.python.org/3.3/library/io.html?highlight=stringio#io.StringIO dice 'El argumento de la nueva línea funciona como el de TextIOWrapper 'y TextIOWrapper dice' si newline es None, el modo universal de nuevas líneas está habilitado '. Pero entonces StringIO posiblemente contradice esto al decir 'El valor predeterminado es no hacer una nueva traducción de línea'. – Calvin

Respuesta

5

¿Qué tal:

file = self.request.get('catalog') 
file = '\n'.join(file.splitlines()) 
catalog = csv.reader(StringIO.StringIO(file),dialect=csv.excel_tab) 

o como se señala en los comentarios, csv.reader() admite la entrada de una lista, por lo que:

file = self.request.get('catalog') 
catalog = csv.reader(file.splitlines(),dialect=csv.excel_tab) 

o si en el futuro request.get permite leer modos:

file = self.request.get('catalog', 'rU') 
catalog = csv.reader(StringIO.StringIO(file),dialect=csv.excel_tab) 
+0

De hecho uso .splitlines() pero trabajo en archivos muy grandes y no es muy rápido. request.get() no admite modos de lectura. – greg

+1

@greg: Entonces estás atrapado. Puedes emularlo haciendo .replace ('\ r \ n', '\ n') si ese es el único final de línea que necesitas cambiar. – theheadofabroom

+4

@greg Tomaría la velocidad y usaré 'catalog = csv.reader (file.splitlines(), dialect = csv.excel_tab)'. (El lector csv puede aceptar una lista de cadenas) – Calvin

4

La solución descrita here debería funcionar. Al definir una clase de iterador de la siguiente manera, que carga el blob de 1MB a la vez, divide las líneas con .splitlines() y luego alimenta las líneas al lector CSV de a una por vez, las nuevas líneas se pueden manejar sin tener que cargar todo el archivo en la memoria.

class BlobIterator: 
    """Because the python csv module doesn't like strange newline chars and 
    the google blob reader cannot be told to open in universal mode, then 
    we need to read blocks of the blob and 'fix' the newlines as we go""" 

    def __init__(self, blob_reader): 
     self.blob_reader = blob_reader 
     self.last_line = "" 
     self.line_num = 0 
     self.lines = [] 
     self.buffer = None 

    def __iter__(self): 
     return self 

    def next(self): 
     if not self.buffer or len(self.lines) == self.line_num + 1: 
      self.buffer = self.blob_reader.read(1048576) # 1MB buffer 
      self.lines = self.buffer.splitlines() 
      self.line_num = 0 

      # Handle special case where our block just happens to end on a new line 
      if self.buffer[-1:] == "\n" or self.buffer[-1:] == "\r": 
       self.lines.append("") 

     if not self.buffer: 
      raise StopIteration 

     if self.line_num == 0 and len(self.last_line) > 0: 
      result = self.last_line + self.lines[self.line_num] + "\n" 
     else: 
      result = self.lines[self.line_num] + "\n" 

     self.last_line = self.lines[self.line_num + 1] 
     self.line_num += 1 

     return result 

A continuación, llamar a este modo:

blob_reader = blobstore.BlobReader(blob_key) 
blob_iterator = BlobIterator(blob_reader) 
reader = csv.reader(blob_iterator) 
+0

funcionó como un encanto. Muchas gracias –

+1

En archivos csv, puede tener caracteres de nueva línea dentro de una sola "celda", si la celda está citada. La técnica de las divisiones() se rompería en ese escenario. – Troy