2012-06-07 29 views
6

So come fare per un file .txt ... ma ora ho qualche problema con un file .csv. Come posso leggere un file CSV dal basso in python?come leggere un file csv in ordine inverso in python

+3

[Cosa hai provato finora?] (Http://mattgemmell.com/2008/12/08/what-have-you-tried/) – JoeFish

+2

sei su linux? 'tac' il file poi lo legge. – dm03514

+0

Mostraci cosa hai per un file '.txt'.È probabile che tu possa usare la stessa tecnica. – NPE

risposta

19

Più o meno allo stesso modo di un file di testo: leggere tutto in una lista e poi andare a ritroso:

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

Se si desidera ottenere fantasia, si potrebbe scrivere un sacco di codice che legge blocchi che iniziano alla fine del file e lavorano all'indietro, emettendo una riga alla volta, e poi lo inviano a csv.reader, ma funzionerà solo con un file che può essere cercato, ovvero i file su disco ma non lo standard input.


Alcuni di noi hanno i file che non rientrano nella memoria, si poteva venire con una soluzione che non richiede la memorizzazione l'intero file in memoria?

Questo è un po 'più complicato. Fortunatamente, tutto il csv.reader prevede un oggetto simile a un iteratore che restituisce una stringa (riga) per chiamata a next(). Così abbiamo afferrare la tecnica di Dario Bacone presentato in "Most efficient way to search the last x lines of a file in python" per leggere le righe di un file a ritroso, senza dover tirare in tutto il file:

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) 

e mangimi reversed_lines nel codice di invertire le linee prima si arriva a csv.reader, eliminando la necessità di reversed e list:

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

C'è una soluzione più Pythonic possibile, che non richiede un'inversione carattere per carattere del blocco in memoria (suggerimento: basta avere al è di indici in cui ci sono linee nel blocco, lo si inverte, e lo si usa per tagliare il blocco) e usa chain su itertools per incollare i cluster di linee da blocchi successivi, ma questo è lasciato come esercizio per il lettore.


vale la pena notare che le reversed_lines() idioma sopra funziona solo se le colonne nel file CSV non contengono a capo.

Aargh! C'è sempre qualcosa. Per fortuna, non è troppo male per risolvere questo 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] 

Naturalmente, sarà necessario cambiare il carattere preventivo se il vostro dialetto CSV non usa ".

+0

è lo stesso? f = open (FilePath, "rb") csvfile = invertito ([riga per riga in csv.reader (f)]) – SirC

+0

Non si deve fare affidamento sulla garbage collection per chiudere i file. Questo non funzionerà molto bene nell'implementazione Python che non usa il conteggio dei riferimenti per la garbage collection, e innesca anche 'ResourceWarning's in CPython 3.2 (se abilitato). –

+1

Un'altra osservazione: non mi piace l'idioma '[line for line in reader]' e preferisco 'list (reader)', che ritengo sia più pertinente. –

0

Basandosi sulla risposta di @ mike-desimone. Ecco una soluzione che fornisce la stessa struttura di un oggetto file python, ma viene letta al contrario, riga per riga:

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

Vai lì. Questo è un semplice programma per invertire le righe da un file 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] 
Problemi correlati