Spróbuj, fileinputjak opisał @ jf-sebastian tutaj . Wydaje się, że pozwala na pracę wiersz po wierszu, za pośrednictwem pliku tymczasowego, wszystko z prostą forskładnią.
Kevin
Odpowiedzi:
205
Najpierw otwórz plik i pobierz wszystkie swoje wiersze z pliku. Następnie ponownie otwórz plik w trybie zapisu i zapisz swoje wiersze z powrotem, z wyjątkiem wiersza, który chcesz usunąć:
with open("yourfile.txt","r")as f:
lines = f.readlines()with open("yourfile.txt","w")as f:for line in lines:if line.strip("\n")!="nickname_to_delete":
f.write(line)
Potrzebujesz strip("\n")znaku nowej linii w porównaniu, ponieważ jeśli twój plik nie kończy się znakiem nowej linii, ostatni też linenie.
@Ooker: Musisz dwukrotnie otworzyć plik (i zamknąć go w międzyczasie), ponieważ w pierwszym trybie jest on „tylko do odczytu”, ponieważ właśnie czytasz bieżące wiersze pliku. Następnie zamykasz go i ponownie otwierasz w „trybie zapisu”, w którym plik jest zapisywalny i zastępujesz zawartość pliku bez linii, którą chciałeś usunąć.
Devin
4
Dlaczego Python nie pozwala nam tego zrobić w jednej linii?
Ooker
5
@Ooker, Kiedy czytasz wiersz, spróbuj wyobrazić sobie kursor poruszający się wzdłuż wiersza podczas czytania. Po przeczytaniu tej linii kursor znajduje się za nią. Kiedy próbujesz pisać do pliku, piszesz, gdzie aktualnie znajduje się kursor. Ponowne otwarcie pliku powoduje zresetowanie kursora.
Waddas
4
Użyj ze związkiem!
Sceluswe,
100
Rozwiązanie tego problemu przy tylko jednym otwarciu:
with open("target.txt","r+")as f:
d = f.readlines()
f.seek(0)for i in d:if i !="line you want to remove...":
f.write(i)
f.truncate()
To rozwiązanie otwiera plik w trybie r / w ("r +") i używa funkcji seek do zresetowania wskaźnika f, a następnie obcięcia, aby usunąć wszystko po ostatnim zapisie.
Działało to bardzo dobrze dla mnie, ponieważ musiałem również użyć pliku blokującego (fcntl). Nie mogłem znaleźć żadnego sposobu na użycie fileinput razem z fcntl.
Easyrider
1
Fajnie byłoby zobaczyć efekty uboczne tego rozwiązania.
user1767754
3
Nie zrobiłbym tego. Jeśli pojawi się błąd w forpętli, otrzymasz częściowo nadpisany plik z zduplikowanymi wierszami lub obciętą do połowy linią. Zamiast tego możesz chcieć f.truncate()zaraz potem f.seek(0). W ten sposób, jeśli pojawi się błąd, skończysz z niekompletnym plikiem. Ale prawdziwym rozwiązaniem (jeśli masz miejsce na dysku) jest wyjście do pliku tymczasowego, a następnie użycie os.replace()lub pathlib.Path(temp_filename).replace(original_filename)zamiana go z oryginałem, gdy wszystko się powiedzie.
Boris
Możesz dodać, i.strip('\n') != "line you want to remove..."jak wspomniano w zaakceptowanej odpowiedzi, że to doskonale rozwiązałoby mój problem. Ponieważ po prostu inic dla mnie nie zrobiłem
Mangohero 1
31
Najlepszą i najszybszą opcją, zamiast przechowywać wszystko na liście i ponownie otwierać plik, aby go zapisać, jest moim zdaniem ponowne zapisanie pliku w innym miejscu.
with open("yourfile.txt","r")as input:with open("newfile.txt","w")as output:for line in input:if line.strip("\n")!="nickname_to_delete":
output.write(line)
Otóż to! W jednej pętli i tylko jednej możesz zrobić to samo. Będzie dużo szybciej.
Zamiast używać zwykłej pętli for możemy skorzystać z wyrażenia generatora. W ten sposób program nie załaduje wszystkich linii z pliku do pamięci, co nie jest dobrym pomysłem w przypadku dużych plików. W danym momencie będzie mieć w pamięci tylko jedną linię. Z generatorem wyrażenie pętli będzie wyglądać następująco,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde
4
@ShriShinde Nie wczytujesz pliku do pamięci podczas wykonywania pętli po obiekcie pliku, więc to rozwiązanie działa identycznie jak Twoja sugestia.
Steinar Lima
Możesz chcieć usunąć oryginalny plik i zmienić nazwę drugiego pliku na nazwę oryginalnego pliku, która w Pythonie w systemie Linux wyglądałaby następująco,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
maks.
6
os.replace(nowość w Pythonie w wersji 3.3) jest bardziej wieloplatformowa niż wywołanie systemowe mv.
7yl4r
Proste i świetne.
JuBaer AD
27
To jest „widelec” od @Lother (którą moim zdaniem należy uznać za właściwą).
W przypadku takiego pliku:
$ cat file.txt
1: october rust
2: november rain
3: december snow
Ten widelec z rozwiązania Lother's działa dobrze:
#!/usr/bin/python3.4with open("file.txt","r+")as f:
new_f = f.readlines()
f.seek(0)for line in new_f:if"snow"notin line:
f.write(line)
f.truncate()
Ulepszenia:
with open, które odrzucają użycie f.close()
jaśniejsze if/elsedo oceny, czy w bieżącej linii nie ma łańcucha
@yifan yes. W przeciwnym razie zamiast nadpisywać plik, dołączysz go do samego siebie (bez wykluczonych linii).
Boris
5
Problem z czytaniem wierszy w pierwszym przebiegu i wprowadzaniem zmian (usuwanie określonych wierszy) w drugim przebiegu polega na tym, że jeśli rozmiary plików są ogromne, zabraknie pamięci RAM. Zamiast tego lepszym podejściem jest czytanie wierszy jeden po drugim i zapisywanie ich w osobnym pliku, eliminując te, których nie potrzebujesz. Uruchomiłem to podejście z plikami o wielkości do 12-50 GB, a użycie pamięci RAM pozostaje prawie stałe. Tylko cykle procesora pokazują przetwarzanie w toku.
To rozwiązanie nie jest agnostyczne dla systemu operacyjnego, a ponieważ OP nie określił systemu operacyjnego, nie ma powodu, aby publikować odpowiedź imo dla systemu Linux.
Steinar Lima
2
Każdy, kto zasugeruje użycie podprocesu do wszystkiego, co można zrobić za pomocą samego Pythona, otrzyma głos przeciw! I +1 do @SteinarLima ... Zgadzam się
Jamie Lindsey,
2
Myślę, że jeśli wczytasz plik do listy, zrób to, co możesz iterować po liście, aby znaleźć pseudonim, którego chcesz się pozbyć. Możesz to zrobić znacznie wydajnie bez tworzenia dodatkowych plików, ale będziesz musiał zapisać wynik z powrotem do pliku źródłowego.
Oto jak mogę to zrobić:
import, os, csv # and other imports you need
nicknames_to_delete =['Nick','Stephen','Mark']
Następnie przejdź do listy, aby dopasować dane wejściowe do usunięcia:
for nick in nicknames_to_delete:try:if nick in nicknames:
nicknames.pop(nicknames.index(nick))else:print(nick +" is not found in the file")exceptValueError:pass
Na koniec zapisz wynik z powrotem do pliku:
with open("nicknames.csv","a")as nicknamesFile:
nicknamesFile.seek(0)
nicknamesFile.truncate()
nicknamesWriter = csv.writer(nicknamesFile)for name in nicknames:
nicknamesWriter.writeRow([str(name)])
nicknamesFile.close()
Ogólnie nie możesz; musisz ponownie napisać cały plik (przynajmniej od momentu zmiany do końca).
W niektórych przypadkach możesz zrobić lepiej niż to -
jeśli wszystkie elementy danych mają taką samą długość i nie są ułożone w określonej kolejności, a znasz przesunięcie elementu, którego chcesz się pozbyć, możesz skopiować ostatni element do elementu, który ma zostać usunięty i skrócić plik przed ostatnim elementem ;
lub możesz po prostu nadpisać fragment danych wartością „to są złe dane, pomiń” lub zachować flagę „ten element został usunięty” w zapisanych elementach danych, tak aby można było oznaczyć go jako usuniętego bez konieczności modyfikowania pliku w inny sposób.
To prawdopodobnie przesada w przypadku krótkich dokumentów (mniej niż 100 KB?).
Prawdopodobnie masz już poprawną odpowiedź, ale oto moja. Zamiast używać listy do zbierania niefiltrowanych danych (jaka readlines()metoda to robi), używam dwóch plików. Jedna służy do przechowywania głównych danych, a druga do filtrowania danych podczas usuwania określonego ciągu. Oto kod:
main_file = open('data_base.txt').read()# your main dataBase file
filter_file = open('filter_base.txt','w')
filter_file.write(main_file)
filter_file.close()
main_file = open('data_base.txt','w')for line in open('filter_base'):if'your data to delete'notin line:# remove a specific string
main_file.write(line)# put all strings back to your db except deletedelse:pass
main_file.close()
Zapisz wiersze pliku na liście, a następnie usuń z listy wiersz, który chcesz usunąć i zapisz pozostałe wiersze do nowego pliku
with open("file_name.txt","r")as f:
lines = f.readlines()
lines.remove("Line you want to delete\n")with open("new_file.txt","w")as new_f:for line in lines:
new_f.write(line)
Jeśli plik nie kończy się znakiem nowej linii, ten kod nie usunie ostatniej linii, nawet jeśli zawiera słowo, które chcesz usunąć.
Boris
0
oto inna metoda usuwania / niektórych linii z pliku:
src_file = zzzz.txt
f = open(src_file,"r")
contents = f.readlines()
f.close()
contents.pop(idx)# remove the line item from list, by line number, starts from 0
f = open(src_file,"w")
contents ="".join(contents)
f.write(contents)
f.close()
Zakładając, że jesteś w stanie załadować pełny plik txt. Następnie definiujesz listę niechcianych pseudonimów, a następnie zastępujesz je pustym ciągiem znaków „”.
# Delete unwanted charactersimport re
# Read, then decode for py2 compat.
path_to_file ='data/nicknames.txt'
text = open(path_to_file,'rb').read().decode(encoding='utf-8')# Define unwanted nicknames and substitute them
unwanted_nickname_list =['SourDough']
text = re.sub("|".join(unwanted_nickname_list),"", text)
nie ma potrzeby budowania dyktatu, po prostu użyjfor nb, line in enumerate(f.readlines())
Dionys
-3
Weź zawartość pliku, podziel go znakiem nowej linii na krotkę. Następnie uzyskaj dostęp do numeru wiersza swojej krotki, dołącz do swojej krotki wyników i nadpisz do pliku.
(1) masz na myśli tuple(f.read().split('\n'))?? (2) „Uzyskaj dostęp do numeru wiersza swojej krotki” i „dołącz do swojej krotki wyników” brzmią raczej tajemniczo; rzeczywisty kod Pythona może być bardziej zrozumiały.
fileinput
jak opisał @ jf-sebastian tutaj . Wydaje się, że pozwala na pracę wiersz po wierszu, za pośrednictwem pliku tymczasowego, wszystko z prostąfor
składnią.Odpowiedzi:
Najpierw otwórz plik i pobierz wszystkie swoje wiersze z pliku. Następnie ponownie otwórz plik w trybie zapisu i zapisz swoje wiersze z powrotem, z wyjątkiem wiersza, który chcesz usunąć:
Potrzebujesz
strip("\n")
znaku nowej linii w porównaniu, ponieważ jeśli twój plik nie kończy się znakiem nowej linii, ostatni teżline
nie.źródło
Rozwiązanie tego problemu przy tylko jednym otwarciu:
To rozwiązanie otwiera plik w trybie r / w ("r +") i używa funkcji seek do zresetowania wskaźnika f, a następnie obcięcia, aby usunąć wszystko po ostatnim zapisie.
źródło
for
pętli, otrzymasz częściowo nadpisany plik z zduplikowanymi wierszami lub obciętą do połowy linią. Zamiast tego możesz chciećf.truncate()
zaraz potemf.seek(0)
. W ten sposób, jeśli pojawi się błąd, skończysz z niekompletnym plikiem. Ale prawdziwym rozwiązaniem (jeśli masz miejsce na dysku) jest wyjście do pliku tymczasowego, a następnie użycieos.replace()
lubpathlib.Path(temp_filename).replace(original_filename)
zamiana go z oryginałem, gdy wszystko się powiedzie.i.strip('\n') != "line you want to remove..."
jak wspomniano w zaakceptowanej odpowiedzi, że to doskonale rozwiązałoby mój problem. Ponieważ po prostui
nic dla mnie nie zrobiłemNajlepszą i najszybszą opcją, zamiast przechowywać wszystko na liście i ponownie otwierać plik, aby go zapisać, jest moim zdaniem ponowne zapisanie pliku w innym miejscu.
Otóż to! W jednej pętli i tylko jednej możesz zrobić to samo. Będzie dużo szybciej.
źródło
(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
os.replace
(nowość w Pythonie w wersji 3.3) jest bardziej wieloplatformowa niż wywołanie systemowemv
.To jest „widelec” od @Lother (którą moim zdaniem należy uznać za właściwą).
W przypadku takiego pliku:
Ten widelec z rozwiązania Lother's działa dobrze:
Ulepszenia:
with open
, które odrzucają użycief.close()
if/else
do oceny, czy w bieżącej linii nie ma łańcuchaźródło
Problem z czytaniem wierszy w pierwszym przebiegu i wprowadzaniem zmian (usuwanie określonych wierszy) w drugim przebiegu polega na tym, że jeśli rozmiary plików są ogromne, zabraknie pamięci RAM. Zamiast tego lepszym podejściem jest czytanie wierszy jeden po drugim i zapisywanie ich w osobnym pliku, eliminując te, których nie potrzebujesz. Uruchomiłem to podejście z plikami o wielkości do 12-50 GB, a użycie pamięci RAM pozostaje prawie stałe. Tylko cykle procesora pokazują przetwarzanie w toku.
źródło
Podobało mi się podejście do wpisywania plików, jak wyjaśniono w tej odpowiedzi: Usuwanie linii z pliku tekstowego (python)
Załóżmy na przykład, że mam plik, który ma puste wiersze i chcę usunąć puste wiersze, oto jak to rozwiązałem:
źródło
Jeśli używasz Linuksa, możesz wypróbować następujące podejście.
Załóżmy, że masz plik tekstowy o nazwie
animal.txt
:Usuń pierwszą linię:
następnie
źródło
Myślę, że jeśli wczytasz plik do listy, zrób to, co możesz iterować po liście, aby znaleźć pseudonim, którego chcesz się pozbyć. Możesz to zrobić znacznie wydajnie bez tworzenia dodatkowych plików, ale będziesz musiał zapisać wynik z powrotem do pliku źródłowego.
Oto jak mogę to zrobić:
Zakładam, że
nicknames.csv
zawiera dane takie jak:Następnie załaduj plik na listę:
Następnie przejdź do listy, aby dopasować dane wejściowe do usunięcia:
Na koniec zapisz wynik z powrotem do pliku:
źródło
Ogólnie nie możesz; musisz ponownie napisać cały plik (przynajmniej od momentu zmiany do końca).
W niektórych przypadkach możesz zrobić lepiej niż to -
jeśli wszystkie elementy danych mają taką samą długość i nie są ułożone w określonej kolejności, a znasz przesunięcie elementu, którego chcesz się pozbyć, możesz skopiować ostatni element do elementu, który ma zostać usunięty i skrócić plik przed ostatnim elementem ;
lub możesz po prostu nadpisać fragment danych wartością „to są złe dane, pomiń” lub zachować flagę „ten element został usunięty” w zapisanych elementach danych, tak aby można było oznaczyć go jako usuniętego bez konieczności modyfikowania pliku w inny sposób.
To prawdopodobnie przesada w przypadku krótkich dokumentów (mniej niż 100 KB?).
źródło
Prawdopodobnie masz już poprawną odpowiedź, ale oto moja. Zamiast używać listy do zbierania niefiltrowanych danych (jaka
readlines()
metoda to robi), używam dwóch plików. Jedna służy do przechowywania głównych danych, a druga do filtrowania danych podczas usuwania określonego ciągu. Oto kod:Mam nadzieję, że okaże się to przydatne! :)
źródło
Zapisz wiersze pliku na liście, a następnie usuń z listy wiersz, który chcesz usunąć i zapisz pozostałe wiersze do nowego pliku
źródło
oto inna metoda usuwania / niektórych linii z pliku:
źródło
Podoba mi się ta metoda wykorzystująca fileinput i metodę „inplace”:
Jest trochę mniej rozwlekły niż inne odpowiedzi i wystarczająco szybki
źródło
Zakładając, że jesteś w stanie załadować pełny plik txt. Następnie definiujesz listę niechcianych pseudonimów, a następnie zastępujesz je pustym ciągiem znaków „”.
źródło
Aby usunąć określoną linię pliku według jej numeru linii :
Zastąp zmienne nazwa_pliku i line_to_delete nazwą twojego pliku i numerem linii, którą chcesz usunąć.
Przykładowe dane wyjściowe :
źródło
for nb, line in enumerate(f.readlines())
Weź zawartość pliku, podziel go znakiem nowej linii na krotkę. Następnie uzyskaj dostęp do numeru wiersza swojej krotki, dołącz do swojej krotki wyników i nadpisz do pliku.
źródło
tuple(f.read().split('\n'))
?? (2) „Uzyskaj dostęp do numeru wiersza swojej krotki” i „dołącz do swojej krotki wyników” brzmią raczej tajemniczo; rzeczywisty kod Pythona może być bardziej zrozumiały.