2013-06-10 8 views
8

ich eine Python-Anwendung auf Datei, die eine SQL-Datei ausgibt:„Backspace“ über letzte Zeichen geschrieben

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

Dies ist nicht in einer for Schleife getan, geschrieben werden als Daten verfügbar wird. Gibt es eine Möglichkeit, das letzte Kommazeichen zurückzusetzen, wenn die Anwendung ausgeführt wird, und es durch ein Semikolon zu ersetzen? Ich bin mir sicher, dass ich eine Workaround erfinden könnte, indem ich das Komma vor dem Zeilenumbruch ausgeben und ein globales Bool verwenden würde, um festzustellen, ob ein bestimmter "Schreibvorgang" der erste Schreibvorgang ist. Ich denke jedoch, dass die Anwendung viel sauberer wäre, wenn ich einfach darüber "zurückgehen" könnte. Natürlich, Python zu sein, gibt es vielleicht einen einfacheren Weg!

Beachten Sie, dass es nicht eine praktikable Lösung in diesem Anwendungsfall ist, jede insert Wertzeile in einer Liste zu haben und dann die Liste zu implodieren.

+0

Sie wollen nicht wirklich dorthin gehen; Sie würden Ihre Ausgabedatei lieber sehr allgemein halten, so dass es eine Pipe, Named Pipe, ein Objekt, das eine TCP-Verbindung oder ein HTTP-Post-Feld usw. darstellt, haben könnte. Einige davon haben Nebenwirkungen und Sie können das letzte Zeichen nicht zurücknehmen . –

+0

@qarma: Danke. In dem speziellen Fall, in dem meine "Ausgabedatei" nicht generisch ist, sondern eine Textdatei, wie ich sie beschrieben habe, gibt es irgendwelche "Nebenwirkungen", vor denen ich mich hüten sollte? Beachten Sie, dass dies auf einem von Debian abgeleiteten Betriebssystem ausgeführt wird. Außerdem bin ich nicht sicher, warum ich meine Ausgabedatei lieber sehr allgemein halten würde, vielleicht könnten Sie das erklären? – dotancohen

Antwort

8

Verwenden suchen Sie den Cursor ein Byte (Zeichen) rückwärts zu bewegen, dann schreiben Sie das neue Zeichen:

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

Dies ist die einfachste Änderung Ihrer aktuellen Code beibehalten („Arbeitscode“ schlägt „ideal Code“) aber es wäre besser, die Situation zu vermeiden.

+1

+1 für Hack. Ich denke, das funktioniert nur für Binärdateien. Wenn die Datei im Textmodus geöffnet wird und das letzte Zeichen ein komplexer Unicode ist, bezweifle ich, dass "seek" weiß, wie viele Bytes zurückzurollen sind. –

+0

Danke, das ist machbar.Tatsächlich werden die Dateien als "a +" geöffnet (es wird erwartet, dass sie nicht vorher existieren). Das 'letzte Zeichen' wird immer das Komma sein. Ist das sicher? – dotancohen

+0

Etwas sicher, ja. Etwas hässlich, ja. – kqr

0

Versuchen Sie, die Datei zu öffnen, um als Binärdatei zu schreiben: 'wb' anstelle von 'w'.

4

Wie wäre es mit dem Hinzufügen der Kommas vor dem Hinzufügen der neuen Zeile?

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() 

Sie erwähnte dies in Ihrer Frage - ich denke, das an einen Betreuer ein viel klarer ist sie Kommas und Überschreiben als suchen.

EDIT: Da die obige Lösung eine globale boolean in Ihrem Code erfordern würde (was nicht erwünscht ist) Sie stattdessen die Datei Schreibverhalten in eine Hilfsklasse wickeln könnte:

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 + "')") 

Diese kapselt die alle SQL-Notation Logik in einer einzigen Klasse, halten den Code lesbar und gleichzeitig den Anrufer-Code zu vereinfachen.

+0

Danke. Das Problem bei diesem Ansatz besteht darin, den globalen "Bool" beizubehalten, der in dieser speziellen Anwendung nicht durchführbar ist. – dotancohen

+0

Ich habe meine Antwort aktualisiert, um Ihnen vorzuschlagen, wie Sie ein globales 'Bool' vermeiden können, indem Sie die Dateioperationen in eine Hilfsklasse einfügen. – seanhodges

+0

Danke Sean. Wenn ich auf der Bühne stehe, dass ich mit der Bereitstellung neuer Funktionen für den Code beginnen kann, werde ich damit umgehen. – dotancohen

0

Verwenden Generatoren, zB:

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 

Für Hardcore-Iterator verwenden, dies sollte Ihnen den Einstieg:

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

Wenn Ihre Daten zwingend notwendig API verwendet, das nicht iterable ist, send() Ihre Daten Generator:

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

Danke qarma. Aus dem gleichen Grund, dass ich kein globales Bool halten kann, um zu wissen, ob ich auf dem "ersten Ausgang" bin, wird ein Generator auch nicht helfen. Ich habe im OP erwähnt, dass das Explodieren eines Wörterbuchs keine praktikable Lösung für diesen Anwendungsfall ist. – dotancohen

Verwandte Themen