Python - write () versus writelines () i połączone łańcuchy

124

Więc uczę się Pythona. Przechodzę przez lekcje i natknąłem się na problem, w którym musiałem skondensować bardzo wiele target.write()w jedną write(), mając "\n"pomiędzy każdą zmienną wejściową użytkownika (obiekt write()).

Wymyśliłem:

nl = "\n"
lines = line1, nl, line2, nl, line3, nl
textdoc.writelines(lines)

Jeśli spróbuję:

textdoc.write(lines)

Otrzymuję błąd. Ale jeśli napiszę:

textdoc.write(line1 + "\n" + line2 + ....)

Wtedy działa dobrze. Dlaczego nie mogę użyć ciągu dla nowej linii w, write()ale mogę go użyć w writelines()?

Python 2.7 Kiedy przeszukiwałem google większość znalezionych zasobów było ponad moją głową, nadal jestem laikiem.

AbeLinkon
źródło
linesw twoim przykładzie nie jest ciągiem. Jest to krotka składająca się z sześciu strun.
Bachsau

Odpowiedzi:

147
  • writelines oczekuje iterowalnych łańcuchów
  • write oczekuje pojedynczego ciągu.

line1 + "\n" + line2łączy te ciągi w jeden ciąg przed przekazaniem go do write.

Pamiętaj, że jeśli masz wiele linii, możesz użyć "\n".join(list_of_lines).

DGH
źródło
50
Dokładniej, writelinesoczekuje iterowalnego pliku. Możesz użyć listy, krotki lub generatora.
Mark Ransom,
Dziękuję za odpowiedź. Zakładam przez nazwę (list_of_lines), że powinienem zrobić listę ciągów i przekazać ją do .join (lista)?
AbeLinkon
9
Dlaczego powinieneś używać writezamiast, writelinesjeśli masz wiele linii? Writelines może działać lepiej, ponieważ nie musi tworzyć tymczasowego połączonego ciągu, po prostu iterując po liniach.
Bouke
@ hBy2Py: dokładnie odwrotnie: stackoverflow.com/a/6165711/281545
Mr_and_Mrs_D
1
Pojedynczy ciąg jest również iterowalny w Pythonie
natbusa
123

Dlaczego nie mogę użyć ciągu znaków dla nowej linii w funkcji write (), ale mogę go użyć w writelines ()?

Pomysł jest następujący: jeśli chcesz napisać pojedynczy ciąg, możesz to zrobić za pomocą write(). Jeśli masz sekwencję ciągów, możesz je wszystkie zapisać za pomocą writelines().

write(arg)oczekuje łańcucha jako argumentu i zapisuje go do pliku. Jeśli podasz listę ciągów, zgłosi to wyjątek (przy okazji pokaż nam błędy!).

writelines(arg)oczekuje iterowalnego argumentu (obiekt iterowalny może być krotką, listą, łańcuchem lub iteratorem w najbardziej ogólnym sensie). Każdy element zawarty w iteratorze powinien być ciągiem. Podałeś krotkę ciągów, więc wszystko zadziałało.

Natura ciągu (ów) nie ma znaczenia dla obu funkcji, tzn. Po prostu zapisują do pliku wszystko, co im podasz. Ciekawe jest to, żewritelines() nie dodaje samodzielnie znaków nowej linii, więc nazwa metody może być dość myląca. W rzeczywistości zachowuje się jak wyimaginowana metoda o nazwie write_all_of_these_strings(sequence).

Poniżej przedstawiono idiomatyczny sposób w Pythonie, aby zapisać listę ciągów do pliku, zachowując każdy ciąg w osobnym wierszu:

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.write('\n'.join(lines))

To zajmie się zamknięciem pliku za Ciebie. Konstrukcja'\n'.join(lines) konkatenuje (łączy) ciągi znaków z listy linesi używa znaku „\ n” jako kleju. Jest to bardziej wydajne niż korzystanie z +operatora.

Zaczynając od tej samej linessekwencji, kończąc na tym samym wyjściu, ale używając writelines():

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.writelines("%s\n" % l for l in lines)

Wykorzystuje to wyrażenie generatora i dynamicznie tworzy ciągi zakończone znakiem nowej linii. writelines()iteruje po tej sekwencji ciągów i zapisuje każdy element.

Edycja: Kolejna kwestia, o której powinieneś wiedzieć:

write()i readlines()istniał przed writelines()wprowadzeniem. writelines()został wprowadzony później jako odpowiednik readlines(), dzięki czemu można było łatwo napisać treść pliku, który został właśnie odczytany przez readlines():

outfile.writelines(infile.readlines())

Naprawdę, to jest główny powód, dla którego writelinesma tak mylącą nazwę. Dzisiaj nie chcemy już używać tej metody. readlines()czyta cały plik do pamięci twojego komputera przed writelines()rozpoczęciem zapisywania danych. Przede wszystkim może to tracić czas. Dlaczego nie zacząć zapisywać części danych podczas czytania innych części? Ale co najważniejsze, takie podejście może pochłaniać dużo pamięci. W skrajnym scenariuszu, w którym plik wejściowy jest większy niż pamięć komputera, takie podejście nawet nie zadziała. Rozwiązaniem tego problemu jest użycie tylko iteratorów. Działający przykład:

with open('inputfile') as infile:
    with open('outputfile') as outfile:
        for line in infile:
            outfile.write(line)

To czyta plik wejściowy wiersz po wierszu. Gdy tylko zostanie odczytana jedna linia, ta linia jest zapisywana do pliku wyjściowego. Mówiąc schematycznie, zawsze jest tylko jedna linia w pamięci (w porównaniu do całej zawartości pliku znajdującej się w pamięci w przypadku podejścia readlines / writelines).

Dr Jan-Philip Gehrcke
źródło
5
@AbeLinkon: Nie popieram tego wniosku. write()i writelines()są w zasadzie równoważne, a ich użycie jest również kwestią osobistego gustu. Należy jednak zauważyć, że w przypadku bardzo długiej listy ciągów (nazywanych lines) pisanie jest mniej wydajne f.write('\n'.join(lines))niż for l in line: f.write('%s\n' % l). W pierwszym przypadku przed zapisaniem w pamięci tworzony jest zupełnie nowy i bardzo długi ciąg. W drugim przypadku dane są zapisywane fragmentarycznie.
Dr Jan-Philip Gehrcke
3
f.write ('\ n'.join (lines)) nie dodał ostatniego nl, kiedy go uruchomiłem.
Jiminion
5
Oczywiście nie zrobiłbyś tego, outf.writelines(inf.readlines())ale raczej outf.writelines(inf). Funkcja nie chcemy używać już to readlines()nie writelines().
moooeeeep
2
@moooeeeep: chociaż nie ma nic złego w funkcjonalności / implementacji programu writelines(), jego semantyka jest, jak wyjaśniono, mniej niż idealna. Dlatego nigdy go nie używałem. I nigdy tego nie przegapiłem.
Dr Jan-Philip Gehrcke
2
@AbeLinkon - może powinieneś rozważyć zaakceptowanie tej odpowiedzi, jest ona wyraźnie lepsza niż ta, którą pierwotnie zaakceptowałeś
Peter M. - oznacza Monikę
-4

jeśli chcesz tylko zapisać i załadować listę, wypróbuj Pickle

Oszczędność marynaty:

with open("yourFile","wb")as file:
 pickle.dump(YourList,file)

i ładowanie:

with open("yourFile","rb")as file:
 YourList=pickle.load(file)
Venya
źródło
-5

Właściwie myślę, że problem polega na tym, że zmienne „linie” są złe. Zdefiniowałeś wiersze jako krotkę, ale uważam, że write () wymaga ciągu. Wszystko, co musisz zmienić, to przecinki na plusy (+).

nl = "\n"
lines = line1+nl+line2+nl+line3+nl
textdoc.writelines(lines)

powinno działać.

Kevin
źródło
-5

Ćwiczenie 16 z książki Zeda Shawa? Możesz użyć znaków ucieczki w następujący sposób:

paragraph1 = "%s \n %s \n %s \n" % (line1, line2, line3)
target.write(paragraph1)
target.close()
Gerald
źródło
Bardzo słabe rozwiązanie. Jeśli chciał złączyć kilka linii w ten sposób, należy zrobić to tak: " \n ".join((line1, line2, line3)).
Bachsau