Prawidłowy sposób zapisu linii do pliku?

1069

Jestem przyzwyczajony do robienia print >>f, "hi there"

Wydaje się jednak, że print >>staje się przestarzałe. Jaki jest zalecany sposób wykonania powyższej linii?

Aktualizacja : Odnośnie wszystkich tych odpowiedzi za pomocą "\n"... czy jest to uniwersalne czy specyficzne dla Uniksa? IE, powinienem robić "\r\n"w systemie Windows?

Jarosław Bułatow
źródło
11
„\ n” nie jest specyficzne dla Uniksa. Gdy plik jest otwierany w trybie tekstowym (domyślnie), jest on automatycznie tłumaczony na poprawne zakończenie wiersza dla bieżącej platformy. Napisanie „\ r \ n” spowoduje powstanie „\ r \ r \ n”, co jest niepoprawne.
D Coetzee,
Wystarczy dodać instrukcję print ord (os.linesep), aby zobaczyć kod ascii (10 w większości systemów UNIX)
Stefan Gruenwald
Jak myślisz, dlaczego jest przestarzałe?
xxx ---

Odpowiedzi:

1152

Powinno to być tak proste, jak:

with open('somefile.txt', 'a') as the_file:
    the_file.write('Hello\n')

Z dokumentacji:

Nie używaj os.linesepjako terminatora linii podczas zapisywania plików otwartych w trybie tekstowym (domyślnie); zamiast tego użyj pojedynczego „\ n” na wszystkich platformach.

Kilka przydatnych lektur:

Johnsyweb
źródło
36
Ten przykład jest lepszy niż przykład otwarcia / zamknięcia. Korzystanie withjest bezpieczniejszym sposobem na zapamiętanie zamknięcia pliku.
Eyal
18
Nie muszę dzwonić the_file.close()?
Hussain
19
nie ty nie: stackoverflow.com/questions/3012488/...
Tal Jerome
@Johnsyweb Dlaczego mówisz „os.linesep. W osie jest wiele dobrych rzeczy!”, Skoro wcześniej poleciłeś go nie używać? Podejrzewam tutaj edycję, która może być przyczyną odrzucenia głosów.
Koń SMith,
1
@ user3226167: To interesujący punkt. Ale dlaczego miałbyś otwierać plik binarny, aby pisać zwykły tekst?
Johnsyweb
961

Należy użyć print()funkcji, która jest dostępna od wersji Python 2.6+

from __future__ import print_function  # Only needed for Python 2
print("hi there", file=f)

W przypadku Python 3 nie jest to potrzebne import, ponieważ print()funkcja jest domyślna.

Alternatywą byłoby użycie:

f = open('myfile', 'w')
f.write('hi there\n')  # python will convert \n to os.linesep
f.close()  # you can omit in most cases as the destructor will call it

Cytowanie z dokumentacji Pythona dotyczące nowych linii:

Na wyjściu, czy znak nowej linii jest None, wszelkie '\n'znaki pisane są przeliczane na domyślny układ separatora linii, os.linesep. Jeśli jest nowa linia '', tłumaczenie nie jest wykonywane. Jeśli nowa linia jest jedną z innych dopuszczalnych wartości, wszelkie '\n'zapisane znaki są tłumaczone na podany ciąg.

sorin
źródło
35
-1 "Jeśli chcesz być pewien, dodaj os.linesep do łańcucha zamiast \n" wymagałoby newline = "" w przeciwnym razie dostaniesz się \r\r\nna Windows. Z os.linesep nie ma w ogóle powodu, aby się wygłupiać.
John Machin
7
@Sorin: Twoja edycja dodawania trybu zapisu jest oczywiście ulepszeniem. Jednak dziwnie pozostajesz nieprzejednany w kwestii os.linesep. Zobacz moją odpowiedź. Nawiasem mówiąc, dokumentacja, którą zacytowałeś, dotyczy wersji 3.x, ale ta część jest również ważna dla wersji 2.x w trybie tekstowym: wszelkie zapisane znaki „\ n” są tłumaczone na domyślny systemowy separator linii, os.linesep * . .. Windows: pisanie os.linesep jest tym samym, co pisanie, \r\nktóre zawiera, \nco jest tłumaczone na os.linesep, co jest \r\nkońcowym wynikiem \r\r\n.
John Machin
7
@John miałeś rację, poprawiłem błąd os.linesep. Dzięki.
sorin
3
Do dołączania nie jest to open('myfile','a')zamiast tego open('myfile','w')?
NeDark,
6
@BradRuderman Jest to część standardu POSIX dla tego, co stanowi „linię” w pliku tekstowym, tzn. Każda linia w pliku tekstowym musi być zakończona nową linią, nawet ostatnią linią.
kołodziej
116

Dokumenty pythonowe polecają w ten sposób:

with open('file_to_write', 'w') as f:
    f.write('file contents\n')

Tak zwykle to robię :)

Oświadczenie z docs.python.org :

Dobrą praktyką jest używanie słowa kluczowego „with” w przypadku obiektów plików. Ma to tę zaletę, że plik jest poprawnie zamykany po zakończeniu pakietu, nawet jeśli po drodze zostanie zgłoszony wyjątek. Jest także znacznie krótszy niż pisanie równoważnych bloków try-last.

j7nn7k
źródło
1
Nie podoba mi się ten sposób, gdy muszę zagnieździć withwewnętrzną pętlę. To sprawia, że ​​ciągle otwieram i zamykam plik podczas pracy w pętli. Może coś mi brakuje, czy to naprawdę wada w tym konkretnym scenariuszu?
Sibbs Gambling
38
Co powiesz na zapętlenie w obrębie?
j7nn7k
@ j7nn7k dla linii na
FD
86

W odniesieniu do os.linesep:

Oto dokładnie niezredagowana sesja interpretera języka Python 2.7.1 w systemie Windows:

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.linesep
'\r\n'
>>> f = open('myfile','w')
>>> f.write('hi there\n')
>>> f.write('hi there' + os.linesep) # same result as previous line ?????????
>>> f.close()
>>> open('myfile', 'rb').read()
'hi there\r\nhi there\r\r\n'
>>>

W systemie Windows:

Zgodnie z oczekiwaniami os.linesep NIE daje tego samego wyniku co '\n'. Nie ma możliwości, aby przyniosło to ten sam rezultat. 'hi there' + os.linesepjest równoważne z 'hi there\r\n', co NIE jest równoważne z 'hi there\n'.

To takie proste: użycie, \nktóre zostanie automatycznie przetłumaczone na os.linesep. Od pierwszego portu Pythona w systemie Windows jest to takie proste.

Nie ma sensu używać os.linesep w systemach innych niż Windows, a wyniki są nieprawidłowe w systemie Windows.

NIE UŻYWAJ os.linesep!

John Machin
źródło
świetny przykład - ciekawy, czy jesteś użytkownikiem ipython? fajne funkcje do formatowania sesji
Alvin
Nie jestem do końca pewien, co chcesz nam tutaj powiedzieć. os.linesep zwróci znak terminu wiersza (lub ciąg znaków) zdefiniowany przez system operacyjny. System Windows domyślnie używa \ r \ n dla zakończeń linii. Jednak rozpoznawany jest pojedynczy \ n. Użycie \ n da w pełni przenośne WYJŚCIE, ale os.linesep nie jest źle w systemie Windows.
Gusdor
5
@Gusdor: Chodzi o to, że jeśli jawnie używasz os.linesepw systemie Windows w trybie tekstowym, wynik \r\r\njest niewłaściwy. „Windows używa ...” jest bez znaczenia. Biblioteka C Runtime (a więc Python) przekładają \nsię \r\nna wyjściu w trybie tekstowym. Inne oprogramowanie może zachowywać się inaczej. NIE jest tak, że całe oprogramowanie działające w systemie Windows rozpoznaje samotność \njako separator linii podczas czytania w trybie tekstowym. Python robi. Edytor tekstu Microsoft Notepad nie.
John Machin
6
Prawdopodobnie ktoś inny to czyta, a nie ty, z jakimś oprogramowaniem myszki miki, które obawia się o dodatkowe \r...
John Machin
2
@Gusdor, czy przychodzisz do Pythona z innego języka, w którym użycie „\ n” powoduje wyświetlenie wyniku „\ n” w oknie zamiast „\ r \ n”? Więc nie ma w nim oczekiwanego przez głupka „\ r” edytory tekstu? Jak mówi John, nie tak zachowuje się Python - „\ n” jest automatycznie zastępowane przez „\ r \ n”, jeśli tak właśnie mówi os.linesep. Dlatego wyraźne powiedzenie os.linesep jest tutaj „złe”. To jest jak Department of Redundancy Department. Tak, możesz to zrobić. Nie, nie chcesz.
ToolmakerSteve,
55

Nie sądzę, że istnieje „poprawny” sposób.

Użyłbym:

with open ('myfile', 'a') as f: f.write ('hi there\n')

Na pamiątkę Tim Toady .

Hyperboreus
źródło
Ale OP może chcieć zapisać dodatkowe pliki do pliku. Tutaj plik zostanie zamknięty, gdy withwyjdzie poza zakres.
Keith
To może być open(..., 'a')lub nawet 'at'.
mtrw
5
Eee, tak. Taki jest pomysł korzystania z. Jeśli chcesz zachować otwarty plik, po prostu zadzwoń na początek i zamknij, gdy skończysz ...
Hyperboreus
1
@mtrw. Prawdziwe. Dołączono OP.
Hyperboreus
1
Jeśli chodzi o pytona, to RIP Tim Toady - i bardzo, bardzo słusznie
Mr_and_Mrs_D
21

W Pythonie 3 jest to funkcja, ale w Pythonie 2 możesz dodać to na górze pliku źródłowego:

from __future__ import print_function

To robisz

print("hi there", file=f)
Keith
źródło
17

Jeśli piszesz dużo danych, a szybkość jest problemem, prawdopodobnie powinieneś iść f.write(...). Zrobiłem szybkie porównanie prędkości i było ono znacznie szybsze niż print(..., file=f)podczas wykonywania dużej liczby zapisów.

import time    

start = start = time.time()
with open("test.txt", 'w') as f:
    for i in range(10000000):
        # print('This is a speed test', file=f)
        # f.write('This is a speed test\n')
end = time.time()
print(end - start)

Średnio writeskończyłem na 2,45 s na moim komputerze, podczas gdy printzajęło to około 4 razy dłużej (9,76 s). To powiedziawszy, w większości rzeczywistych scenariuszy nie będzie to problemem.

Jeśli zdecydujesz się iść z print(..., file=f)tym, prawdopodobnie okaże się, że będziesz chciał od czasu do czasu wyłączyć nową linię lub zastąpić ją inną. Można tego dokonać ustawiając opcjonalny endparametr, np.;

with open("test", 'w') as f:
    print('Foo1,', file=f, end='')
    print('Foo2,', file=f, end='')
    print('Foo3', file=f)

Jakikolwiek sposób wybierzesz, sugeruję użycie, withponieważ znacznie ułatwia odczytanie kodu.

Aktualizacja : Ta różnica w wydajności jest wyjaśniona faktem, że writejest wysoce buforowana i powraca zanim nastąpi jakikolwiek zapis na dysk (zobacz tę odpowiedź ), podczas gdy print(prawdopodobnie) używa buforowania linii. Prostym testem byłoby sprawdzenie wydajności również przy długich zapisach, gdzie wady (pod względem prędkości) buforowania linii byłyby mniej wyraźne.

start = start = time.time()
long_line = 'This is a speed test' * 100
with open("test.txt", 'w') as f:
    for i in range(1000000):
        # print(long_line, file=f)
        # f.write(long_line + '\n')
end = time.time()

print(end - start, "s")

Różnica wydajności staje się teraz znacznie mniej wyraźna, ze średnim czasem 2,20 s dla write3,10 s dla print. Jeśli musisz połączyć kilka ciągów, aby uzyskać tę dłuższą wydajność linii, ucierpi, więc przypadki użycia, które printbyłyby bardziej wydajne, są nieco rzadkie.

Robin Keskisarkka
źródło
10

Od wersji 3.5 możesz również użyć pathlibdo tego celu:

Path.write_text(data, encoding=None, errors=None)

Otwórz plik wskazany w trybie tekstowym, zapisz do niego dane i zamknij plik:

import pathlib

pathlib.Path('textfile.txt').write_text('content')
Johnson
źródło
5

Kiedy powiedziałeś „Linia”, oznacza to niektóre zserializowane znaki, które kończą się na „\ n”. Linia powinna być w pewnym momencie ostatnia, więc powinniśmy rozważyć „\ n” na końcu każdej linii. Oto rozwiązanie:

with open('YOURFILE.txt', 'a') as the_file:
    the_file.write("Hello")

w trybie dołączania po każdym zapisie kursor przechodzi do nowej linii, jeśli chcesz użyć wtrybu, powinieneś dodać \nznaki na końcu write()funkcji:

the_file.write("Hello\n")
Reza Tanzifi
źródło
1
„w trybie dołączania po każdym zapisie kursor przechodzi do nowej linii” - nie, nie jest.
W
3

Można również użyć iomodułu jak w:

import io
my_string = "hi there"

with io.open("output_file.txt", mode='w', encoding='utf-8') as f:
    f.write(my_string)
kmario23
źródło
1

Do pisania tekstu w pliku w kolbie można użyć:

filehandle = open("text.txt", "w")
filebuffer = ["hi","welcome","yes yes welcome"]
filehandle.writelines(filebuffer)
filehandle.close()
Sashini Hettiarachchi
źródło
0

Możesz także spróbować filewriter

pip install filewriter

from filewriter import Writer

Writer(filename='my_file', ext='txt') << ["row 1 hi there", "row 2"]

Zapisuje w my_file.txt

Wykonuje iterację lub obiekt z __str__obsługą.

Emin Bugra Saral
źródło
0

Kiedy muszę dużo pisać nowe linie, definiuję lambda, która korzysta z printfunkcji:

out = open(file_name, 'w')
fwl = lambda *x, **y: print(*x, **y, file=out) # FileWriteLine
fwl('Hi')

Podejście to ma tę zaletę, że może wykorzystywać wszystkie funkcje dostępne w printfunkcji.

Aktualizacja: Jak wspomniał Georgy w sekcji komentarzy, można ulepszyć ten pomysł za pomocą partialfunkcji:

from functools import partial
fwl = partial(print, file=out)

IMHO, jest to bardziej funkcjonalne i mniej tajemnicze podejście.

MxNx
źródło
2
Albo inny (prawdopodobnie czystsze) sposób to napisać: from functools import partial; fwl = partial(print, file=out).
Georgy,
@Georgy Twoje podejście jest tak dobre, że można je podać jako nową odpowiedź.
MxNx
1
Pomysł jest taki sam jak twój, tylko implementacja jest nieco inna. Jeśli chcesz, możesz dodać go w edycji do swojej odpowiedzi. Nic mi nie jest.
Georgy,