2013-06-10 16 views
8

Ho un'applicazione Python che genera un file SQL:"Backspace" rispetto allo scorso carattere scritto a file

sql_string = "('" + name + "', " + age + "')," 
output_files['sql'].write(os.linesep + sql_string) 
output_files['sql'].flush() 

Questo non è fatto in un ciclo for, è scritto come dati diventa disponibile. C'è un modo per 'backspace' sull'ultimo carattere virgola quando l'applicazione viene eseguita in esecuzione e per sostituirla con un punto e virgola? Sono sicuro che potrei inventare una soluzione alternativa emettendo la virgola prima della nuova riga e usando un Bool globale per determinare se una particolare "scrittura" è la prima scrittura. Tuttavia, penso che l'applicazione sarebbe molto più pulita se potessi semplicemente "backspace" su di esso. Certo, essendo Python forse c'è un modo più semplice!

Si noti che avere ciascuna riga di valore insert in un elenco e quindi implodere l'elenco non è una soluzione valida in questo caso d'uso.

+0

In realtà non si vuole andare lì; preferisci mantenere il tuo file di output molto generico, in modo che possa essere un pipe, named pipe, un oggetto che rappresenta una connessione TCP o un campo HTTP post, ecc. Alcuni di questi hanno effetti collaterali e non puoi riprendere l'ultimo carattere . –

+0

@qarma: Grazie. Nel caso specifico in cui il mio 'file di output' non è generico ma piuttosto un file di testo come ho descritto, ci sono degli "effetti collaterali" di cui dovrei essere prudente? Si noti che questo sarà in esecuzione su un SO derivato da Debian. Inoltre, non sono sicuro del perché preferirei mantenere il mio file di output molto generico, forse potresti chiarire? – dotancohen

risposta

8

Usa cercano di spostare il cursore di un byte (carattere) a ritroso, quindi scrivere il nuovo carattere:

f.seek(-1, os.SEEK_CUR) 
f.write(";") 

Questo è il cambiamento più semplice, mantenendo il codice corrente ("il codice di lavoro" batte "Codice ideale") ma sarebbe meglio evitare la situazione.

+1

+1 per hack. Immagino che funzioni solo per i file binari. Se il file viene aperto in modalità testo e l'ultimo carattere è unicode complesso, dubito che 'seek' sappia quanti byte eseguire il rollback. –

+0

Grazie, questo potrebbe essere fattibile.Infatti, i file sono aperti come 'a +' (è previsto che non esistano prima). L''ultimo carattere' sarà sempre la virgola. È sicuro? – dotancohen

+0

Alquanto sicuro, sì. Un po 'brutto, sì. – kqr

0

Prova ad aprire il file per scrivere come binario: "wb" anziché "w".

4

Come aggiungere le virgole prima di aggiungere la nuova linea?

first_line = True 

... 

sql_string = "('" + name + "', " + age + "')" 

if not first_line: 
    output_files['sql'].write(",") 
first_line = False 

output_files['sql'].write(os.linesep + sql_string) 
output_files['sql'].flush() 

... 

output_files['sql'].write(";") 
output_files['sql'].flush() 

hai parlato di questo nella sua interrogazione - credo che questo è un molto più chiara ad un manutentore che cercare virgole e sovrascrivendo.

EDIT: Dal momento che la soluzione sopra richiederebbe un valore booleano globale nel codice (che non è auspicabile) si potrebbe invece avvolgere il comportamento di scrittura file in una classe di supporto:

class SqlFileWriter: 

    first_line = True 

    def __init__(self, file_name): 
     self.f = open(file_name) 

    def write(self, sql_string): 
     if not self.first_line: 
      self.f.write(",") 
     self.first_line = False 

     self.f.write(os.linesep + sql_string) 
     self.f.flush() 

    def close(self): 
     self.f.write(";") 
     self.f.close() 


output_files['sql'] = SqlFileWriter("myfile.sql") 
output_files['sql'].write("('" + name + "', '" + age + "')") 

Questo racchiude tutta la notazione SQL logica in una singola classe, mantenendo il codice leggibile e allo stesso tempo semplificando il codice del chiamante.

+0

Grazie. Il problema con questo approccio è il mantenimento del "bool" globale che non è fattibile in questa particolare applicazione. – dotancohen

+0

Ho aggiornato la mia risposta per suggerire come evitare un 'bool' globale avvolgendo le operazioni sui file in una classe helper. – seanhodges

+0

Grazie, Sean. Quando salirò sul palco posso iniziare a distribuire nuove funzionalità al codice, lo gestirò in questo modo. – dotancohen

0

Utilizzare i generatori, per esempio:

def with_separator(data, sep): 
    first = True: 
    for datum in data: 
     if first: 
      first = False 
     else: 
      yield sep 
     yield datum 

with open("sdfasdfas", "w") as outf: 
    for x in with_separator(sql_get_rows(), ",\n"): 
     outf.write(x) 
     # flush if needed 

Per uso iteratore hardcore, questo dovrebbe iniziare:

In [11]: list(itertools.imap("".join, itertools.izip(itertools.chain([""], itertools.repeat(",\n")), "abc"))) 
Out[11]: ['a', ',\nb', ',\nc'] 

Se i dati utilizza API imperativo, che non è iterabile, send() i dati di generatore:

def write_with_separator(filename, sep): 
    with file(filename, "w"): 
     first = True 
     yield None 
     while True: 
      datum = yield None 
      if first: 
       first = False 
      else: 
       fout.write(sep) 
      fout.write(datum) 
      # flush if needed 

writer = write_with_separator("somefilename", ",\n") 
writer.next() # can't send to just-started generator 

# to be called when you get data 
for row in sql_get_rows(): 
    writer.send(row) 
+0

Grazie qarma. Tuttavia, per lo stesso motivo per cui non riesco a mantenere un bool globale per sapere se sono in "first_output", neanche un generatore può essere d'aiuto. Ho menzionato nel PO che l'esplosione di un dizionario non è una soluzione fattibile per questo caso d'uso. – dotancohen

Problemi correlati