2013-06-10 11 views
8

Mam aplikacji Pythona, która wyprowadza plik SQL:„Backspace” w ciągu ostatniego znaku zapisane do pliku

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

nie zostanie to zrobione w for pętli, jest napisane jak dane będą dostępne. Czy istnieje sposób "backspace" na ostatni znak przecinka po uruchomieniu aplikacji i zastąpienie go średnikiem? Jestem pewien, że mógłbym wymyślić jakiś obejście, wypisując przecinek przed znakiem nowej linii i używając globalnego Bool, aby określić, czy jakikolwiek konkretny "zapis" jest pierwszym napisem. Jednak myślę, że aplikacja byłaby o wiele czystsza, gdybym mógł po prostu "cofnąć się" nad nią. Oczywiście bycie Pythonem może być łatwiejsze!

Należy pamiętać, że posiadanie każdej linii wartości insert na liście, a następnie implikowanie listy nie jest wykonalnym rozwiązaniem w tym przypadku użycia.

+0

Tak naprawdę nie chcesz tam iść; wolisz, aby twój plik wyjściowy był bardzo ogólny, aby mógł być potokiem, nazwany potokiem, obiektem reprezentującym połączenie TCP lub polem poczty HTTP itp. Niektóre z nich mają skutki uboczne i nie można cofnąć ostatniej postaci . –

+0

@qarma: Dziękuję. W konkretnym przypadku, w którym mój "plik wyjściowy" nie jest generyczny, ale raczej plik tekstowy, jak opisałem, czy są jakieś "skutki uboczne", które powinienem być ostrożni? Zauważ, że będzie działać na systemie pochodnym od Debiana. Ponadto nie jestem pewien, dlaczego wolałbym zachować bardzo ogólny format pliku wynikowego, być może mógłbyś to wyjaśnić? – dotancohen

Odpowiedz

8

Zastosowanie starają się przenieść kursor jeden bajt (znak) do tyłu, a następnie napisać nową postać:

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

To najprostszy zmiana, utrzymując swój aktualny kod („kod roboczy” bije „idealnego kod”), ale lepiej byłoby uniknąć sytuacji.

+1

+1 za włamanie. Wyobrażam sobie, że działa to tylko w przypadku plików binarnych. Jeśli plik jest otwarty w trybie tekstowym, a ostatni znak jest złożonym unicode, wątpię, aby 'seek' wiedział, ile bajtów ma wycofać. –

+0

Dziękuję, to może być wykonalne.W rzeczywistości pliki są otwierane jako 'a +' (oczekuje się, że wcześniej nie istnieją). "Ostatnia postać" zawsze będzie przecinkiem. Czy to jest bezpieczne? – dotancohen

+0

Trochę bezpieczne, tak. Trochę brzydko, tak. – kqr

0

Spróbuj otworzyć plik, aby zapisać jako plik binarny: "wb" zamiast "w".

4

Co powiecie na dodanie przecinków przed dodaniem nowego wiersza?

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

Zrobiłeś wspomnieć o tym w swoim pytaniu - Myślę, że to jest o wiele jaśniejsze niż do opiekuna poszukuje przecinki i ich nadpisanie.

EDIT: Ponieważ powyższe rozwiązanie wymagałoby globalną wartość logiczną w kodzie (co nie jest pożądane) można zamiast zawijać zachowanie zapisu plików do klasy pomocnika:

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

Ten oddaje cały zapis SQL logika w jedną klasę, zachowując czytelność kodu i jednocześnie upraszczając kod wywołujący.

+0

Dziękuję. Problem z tym podejściem polega na utrzymywaniu globalnego 'bool', które nie jest możliwe w tej konkretnej aplikacji. – dotancohen

+0

Zaktualizowałem swoją odpowiedź, aby zasugerować, w jaki sposób można uniknąć globalnego 'bool' przez zawijanie operacji na plikach w klasie pomocnika. – seanhodges

+0

Dziękuję Sean. Gdy dojdę do etapu, w którym mogę rozpocząć wdrażanie nowych funkcji do kodu, poradzę sobie z tym w ten sposób. – dotancohen

0

Stosować generatory, np:

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 

Dla zapalonych użycia iteratora, powinno to możesz zacząć:

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

Jeśli dane wykorzystuje imperatyw API, które nie jest iterable, send() dane do 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

Dziękuję qarma. Jednak z tego samego powodu, dla którego nie mogę utrzymać globalnej wartości bool, aby wiedzieć, czy jestem na "first_output", generator też nie pomoże. Wspomniałem w OP, że eksplodowanie słownika nie jest wykonalnym rozwiązaniem dla tego przypadku użycia. – dotancohen

Powiązane problemy