2016-02-17 9 views
8

Sto scrivendo uno script per elaborare file EDI X12, che vorrei ripetere riga per riga. I file sono composti da una sequenza di record distinti, ciascuno terminato da un carattere speciale (ad esempio ~, ma vedere di seguito). I file possono essere di grandi dimensioni (> 100 MB), quindi non voglio leggere tutto e dividerlo. I record non sono separati da una nuova riga; leggere nella prima riga probabilmente leggerebbe l'intero file. I file sono tutti-ASCII.Leggi file fino a un carattere

Python prevede chiaramente la lettura di un file fino a un determinato carattere, purché tale carattere sia una nuova riga. Mi piacerebbe fare la stessa cosa con un personaggio arbitrario. Presumo che la lettura per riga sia implementata tramite buffering. Potrei implementare il mio lettore bufferizzato, ma preferirei evitare il codice extra e il sovraccarico se esiste una soluzione migliore.

Nota: ho visto alcune domande simili, ma sembravano tutti concludere che si dovrebbe leggere il file nella riga, presumendo che le linee sarebbero di dimensioni ragionevoli. In questo caso, l'intero file sarà probabilmente una riga.

Modifica: il carattere di terminazione del segmento è qualunque sia il 106 ° byte del file. Non è noto prima che lo script venga invocato.

+3

Forse usate '.read (some_reasonable_number)' sul puntatore del file e cercate il risultato fino a trovare '~', e se lo fate, '.seek()' indietro? – L3viathan

+0

Credo che tu abbia ragione riguardo al lettore bufferizzato, e sfortunatamente l'argomento 'newline' in' open' non consente '~'. –

+2

Temo che dovrai scrivere un'implementazione dedicata su un io.BytesIO, leggendo il file in blocchi e inviando indietro * line * terminato con il tuo particolare delimitatore ... –

risposta

2

Questo è ancora lungi dall'essere ottimale, ma sarebbe un'implementazione puro Python di un buffer molto semplice:

def my_open(filename, char): 
    with open(filename) as f: 
     old_fb="" 
     for file_buffer in iter(lambda: f.read(1024), ''): 
      if old_fb: 
       file_buffer = old_fb + file_buffer 
      pos = file_buffer.find(char) 
      while pos != -1 and file_buffer: 
       yield file_buffer[:pos] 
       file_buffer = file_buffer[pos+1:] 
       pos = file_buffer.find(char) 
      old_fb = file_buffer 
     yield old_fb 

# Usage: 
for line in my_open("weirdfile", "~"): 
    print(line) 
4

Se non ci stanno per essere a capo nel file per cominciare, di trasformare il file prima di tubazioni nella tua script Python, ad esempio:

tr '~' '\n' < source.txt | my-script.py 

Quindi utilizzare readline(), readlines() o for line in file_object: a seconda dei casi .

+1

E se il programma prende il file dalla riga di comando , non stdin, almeno in bash puoi usare la sostituzione di processo: 'my-script.py <(tr '~' '\ n' ShadowRanger

+0

Il terminatore del segmento non è noto al momento del richiamo dello script. Ho chiarito la domanda originale. Inoltre, questo script verrà eseguito in un ambiente Windows con cui non ho molta familiarità, quindi gli strumenti UNIX standard potrebbero non essere disponibili. Inoltre, la sostituzione del processo non avrebbe comunque messo in pratica l'intero contenuto del file in un buffer se lo script Python non lo leggeva abbastanza velocemente? –

0

Forse avete bisogno di qualcosa di simile

avvertimento: codice non ottimizzato

class File(object): 

    def __init__(self, f): 
     self.file = f 

    def readline(self, delimiter='~'): 
     buffer = '' 
     while True: 
      b = self.file.read(1) 
      buffer += b 
      if b: 
       if b == delimiter: 
        yield buffer 
        buffer = '' 
      else: 
       yield buffer 
       break