2012-06-07 50 views
6

Sé cómo hacer para un archivo .txt ... pero ahora tengo algunos problemas para un archivo .csv. ¿Cómo puedo leer un archivo csv desde abajo en Python?cómo leer un archivo csv en orden inverso en python

+3

[¿Qué has intentado hasta ahora?] (Http://mattgemmell.com/2008/12/08/what-have-you-tried/) – JoeFish

+2

estás en Linux? 'tac' el archivo luego léelo. – dm03514

+0

Muéstranos lo que tienes para un archivo '.txt'.Lo más probable es que puedas usar la misma técnica. – NPE

Respuesta

19

Más o menos la misma forma que para un archivo de texto: leer todo el asunto en una lista y luego ir hacia atrás:

import csv 
with open('test.csv', 'r') as textfile: 
    for row in reversed(list(csv.reader(textfile))): 
     print ', '.join(row) 

Si usted desea conseguir la suposición, se podría escribir una gran cantidad de código que lee bloquea comenzando al final del archivo y trabajando hacia atrás, emitiendo una línea a la vez, y luego lo alimenta a csv.reader, pero eso solo funcionará con un archivo que se puede buscar, es decir, archivos de disco pero no de entrada estándar.


Algunos de nosotros tenemos archivos que no caben en la memoria, alguien podría venir con una solución que no requiere almacenar el archivo completo en la memoria?

Eso es un poco más complicado. Afortunadamente, todo csv.reader espera que sea un objeto similar a un iterador que devuelve una cadena (línea) por llamada al next(). Así que agarra la técnica de Darío tocino presenta en "Most efficient way to search the last x lines of a file in python" para leer las líneas de un archivo hacia atrás, sin tener que tirar de todo el archivo:

import os 

def reversed_lines(file): 
    "Generate the lines of file in reverse order." 
    part = '' 
    for block in reversed_blocks(file): 
     for c in reversed(block): 
      if c == '\n' and part: 
       yield part[::-1] 
       part = '' 
      part += c 
    if part: yield part[::-1] 

def reversed_blocks(file, blocksize=4096): 
    "Generate blocks of file's contents in reverse order." 
    file.seek(0, os.SEEK_END) 
    here = file.tell() 
    while 0 < here: 
     delta = min(blocksize, here) 
     here -= delta 
     file.seek(here, os.SEEK_SET) 
     yield file.read(delta) 

y alimentar reversed_lines en el código para revertir las líneas antes llegan a csv.reader, eliminando la necesidad de reversed y list:

import csv 
with open('test.csv', 'r') as textfile: 
    for row in csv.reader(reversed_lines(textfile)): 
     print ', '.join(row) 

Hay una solución más Pythonic posible, que no requiere una inversión de carácter por carácter del bloque en la memoria (pista: acaba de obtener Alabama ist de índices donde hay extremos de línea en el bloque, inviértalo y utilícelo para cortar el bloque), y usa chain de itertools para unir los conglomerados de líneas de sucesivos bloques, pero eso queda como ejercicio para el lector.


Vale la pena señalar que los reversed_lines() modismo anterior sólo funciona si las columnas del archivo CSV no contienen nuevas líneas.

Aargh! Siempre hay algo. Por suerte, no es demasiado mala para solucionar este problema:

def reversed_lines(file): 
    "Generate the lines of file in reverse order." 
    part = '' 
    quoting = False 
    for block in reversed_blocks(file): 
     for c in reversed(block): 
      if c == '"': 
       quoting = not quoting 
      elif c == '\n' and part and not quoting: 
       yield part[::-1] 
       part = '' 
      part += c 
    if part: yield part[::-1] 

Por supuesto, tendrá que cambiar el carácter de comillas si su dialecto CSV no utiliza ".

+0

¿es lo mismo? f = abrir (FilePath, "rb") csvfile = invertido ([línea para la línea en csv.reader (f)]) – SirC

+0

No debe confiar en la recolección de elementos no utilizados para cerrar sus archivos. Esto no funcionará demasiado bien en la implementación de Python que no utiliza el recuento de referencias para la recolección de basura, y también desencadena 'ResourceWarning's en CPython 3.2 (si está habilitado). –

+1

Otra observación: no me gusta el modismo '[line for line in reader]', y prefiero 'list (reader)', que creo que va más al grano. –

0

Basándose en la respuesta de @ mike-desimone. Aquí hay una solución que proporciona la misma estructura que un objeto de archivo python, pero se lee en reversa, línea por línea:

import os 

class ReversedFile(object): 
    def __init__(self, f, mode='r'): 
     """ 
     Wraps a file object with methods that make it be read in reverse line-by-line 

     if ``f`` is a filename opens a new file object 

     """ 
     if mode != 'r': 
      raise ValueError("ReversedFile only supports read mode (mode='r')") 

     if not type(f) == file: 
      # likely a filename 
      f = open(f) 

     self.file = f 
     self.lines = self._reversed_lines() 

    def _reversed_lines(self): 
     "Generate the lines of file in reverse order." 
     part = '' 
     for block in self._reversed_blocks(): 
      for c in reversed(block): 
       if c == '\n' and part: 
        yield part[::-1] 
        part = '' 
       part += c 
     if part: yield part[::-1] 

    def _reversed_blocks(self, blocksize=4096): 
     "Generate blocks of file's contents in reverse order." 
     file = self.file 

     file.seek(0, os.SEEK_END) 
     here = file.tell() 
     while 0 < here: 
      delta = min(blocksize, here) 
      here -= delta 
      file.seek(here, os.SEEK_SET) 
      yield file.read(delta) 


    def __getattribute__(self, name): 
     """ 
     Allows for the underlying file attributes to come through 

     """ 
     try: 
      # ReversedFile attribute 
      return super(ReversedFile, self).__getattribute__(name) 
     except AttributeError: 
      # self.file attribute 
      return getattr(self.file, name) 

    def __iter__(self): 
     """ 
     Creates iterator 

     """ 
     return self 

    def seek(self): 
     raise NotImplementedError('ReversedFile does not support seek') 

    def next(self): 
     """ 
     Next item in the sequence 

     """ 
     return self.lines.next() 

    def read(self): 
     """ 
     Returns the entire contents of the file reversed line by line 

     """ 
     contents = '' 

     for line in self: 
      contents += line 

     return contents 

    def readline(self): 
     """ 
     Returns the next line from the bottom 

     """ 
     return self.next() 

    def readlines(self): 
     """ 
     Returns all remaining lines from the bottom of the file in reverse 

     """ 
     return [x for x in self] 
0

Haga clic aquí. Este es un programa simple para invertir las filas de un archivo CSV.

import csv 
BC_file = open('Master.csv', 'rb') 
BC_reader = csv.reader(BC_file) 
next(BC_reader) 
for row in reversed(list(BC_reader)): 
    print row[0] 
Cuestiones relacionadas