menggunakan Python untuk menghapus baris tertentu dalam file

145

Katakanlah saya memiliki file teks penuh nama panggilan. Bagaimana saya bisa menghapus nama panggilan tertentu dari file ini, menggunakan Python?

SourD
sumber
1
Coba fileinputseperti yang dijelaskan oleh @ jf-sebastian di sini . Tampaknya memungkinkan Anda untuk bekerja baris demi baris, melalui file sementara, semua dengan forsintaksis sederhana .
Kevin

Jawaban:

205

Pertama, buka file dan dapatkan semua baris Anda dari file. Kemudian buka kembali file dalam mode tulis dan tulis baris Anda kembali, kecuali baris yang ingin Anda hapus:

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)

Anda perlu strip("\n")karakter baris baru dalam perbandingan karena jika file Anda tidak berakhir dengan karakter baris baru yang terakhir linetidak akan baik.

houbysoft
sumber
2
mengapa kita harus membuka dan menutupnya dua kali?
Ooker
3
@Ooker: Anda harus membuka file dua kali (dan menutupnya di antara) karena dalam mode pertama itu "read-only" karena Anda hanya membaca di baris saat ini dalam file. Anda kemudian menutupnya dan membukanya kembali dalam "mode tulis", di mana file dapat ditulisi dan Anda mengganti konten file tanpa baris yang ingin Anda hapus.
Devin
4
Mengapa Python tidak mengizinkan kami melakukan ini dalam satu baris?
Ooker
5
@Ooker, Saat Anda membaca sebuah baris, coba bayangkan sebuah kursor bergerak sepanjang garis itu saat dibaca. Setelah baris itu dibaca, kursor sekarang melewatinya. Ketika Anda mencoba menulis ke dalam file yang Anda tulis di mana kursor saat ini. Dengan membuka kembali file, Anda mengatur ulang kursor.
Waddas
4
Gunakan dengan senyawa!
Sceluswe
101

Solusi untuk masalah ini dengan hanya satu buka:

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

Solusi ini membuka file dalam mode r / w ("r +") dan memanfaatkan pencarian untuk mengatur ulang f-pointer kemudian memotong untuk menghapus semuanya setelah penulisan terakhir.

Lebih rendah
sumber
2
Ini bekerja sangat baik bagi saya, karena saya harus menggunakan lockfile juga (fcntl). Saya tidak bisa menemukan cara untuk menggunakan fileinput bersama dengan fcntl.
Easyrider
1
Akan menyenangkan melihat beberapa efek samping dari solusi ini.
user1767754
3
Saya tidak akan melakukan ini. Jika Anda mendapatkan kesalahan dalam forloop, Anda akan berakhir dengan file yang ditimpa sebagian, dengan garis duplikat atau garis terpotong setengah. Anda mungkin ingin f.truncate()benar setelahnya f.seek(0). Dengan begitu jika Anda mendapatkan kesalahan Anda hanya akan berakhir dengan file yang tidak lengkap. Tetapi solusi sebenarnya (jika Anda memiliki ruang disk) adalah untuk output ke file sementara dan kemudian menggunakan os.replace()atau pathlib.Path(temp_filename).replace(original_filename)untuk menukar dengan yang asli setelah semuanya berhasil.
Boris
Mungkin Anda menambahkan i.strip('\n') != "line you want to remove..."seperti yang disebutkan dalam jawaban yang diterima, itu akan menyelesaikan masalah saya dengan sempurna. Karena itidak melakukan apa pun untuk saya
Mangohero1
31

Pilihan terbaik dan tercepat, daripada menyimpan segala sesuatu dalam daftar dan membuka kembali file untuk menulisnya, menurut pendapat saya untuk menulis ulang file di tempat lain.

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)

Itu dia! Dalam satu putaran dan satu-satunya Anda dapat melakukan hal yang sama. Ini akan jauh lebih cepat.

Barnabe
sumber
Alih-alih menggunakan normal untuk loop kita dapat menggunakan Generator Expression. Dengan cara ini program tidak akan memuat semua baris dari file ke memori yang bukan ide yang baik jika ada file besar. Itu hanya akan memiliki satu baris dalam memori pada suatu waktu. Dengan ekspresi generator untuk loop akan terlihat seperti,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde
4
@ShriShinde Anda juga tidak membaca file ke dalam memori saat membalikkan objek file, jadi solusi ini berfungsi sama dengan saran Anda.
Steinar Lima
Anda mungkin ingin menghapus file asli dan mengganti nama file kedua dengan nama file asli, yang dengan Python pada OS Linux akan terlihat seperti ini,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max
6
os.replace(baru dalam python v 3.3) lebih lintas-platform daripada panggilan sistem mv.
7yl4r
Sederhana dan hebat.
JuBaer AD
27

Ini adalah "fork" dari jawaban @Lother (yang saya yakin harus dianggap jawaban yang benar).


Untuk file seperti ini:

$ cat file.txt 
1: october rust
2: november rain
3: december snow

Garpu dari solusi Lother ini bekerja dengan baik:

#!/usr/bin/python3.4

with open("file.txt","r+") as f:
    new_f = f.readlines()
    f.seek(0)
    for line in new_f:
        if "snow" not in line:
            f.write(line)
    f.truncate()

Perbaikan:

  • with open, yang membuang penggunaan f.close()
  • lebih jelas if/elseuntuk mengevaluasi jika string tidak ada di baris saat ini
ivanleoncz
sumber
Jika f.seek (0) diperlukan?
yifan
@ ya, ya. Jika tidak, alih-alih menimpa file, Anda akan menambahkan file itu sendiri (tanpa baris yang Anda kecualikan).
Boris
5

Masalah dengan membaca baris pada pass pertama dan membuat perubahan (menghapus baris tertentu) pada pass kedua adalah bahwa jika ukuran file Anda besar, Anda akan kehabisan RAM. Sebagai gantinya, pendekatan yang lebih baik adalah membaca baris, satu per satu, dan menuliskannya ke dalam file terpisah, menghilangkan yang tidak Anda butuhkan. Saya telah menjalankan pendekatan ini dengan file sebesar 12-50 GB, dan penggunaan RAM tetap hampir konstan. Hanya siklus CPU yang menunjukkan proses yang sedang berlangsung.

Kingz
sumber
2

Saya menyukai pendekatan fileinput seperti yang dijelaskan dalam jawaban ini: Menghapus baris dari file teks (python)

Katakan misalnya saya punya file yang memiliki baris kosong di dalamnya dan saya ingin menghapus baris kosong, berikut cara saya menyelesaikannya:

import fileinput
import sys
for line_number, line in enumerate(fileinput.input('file1.txt', inplace=1)):
    if len(line) > 1:
            sys.stdout.write(line)

Catatan: Baris kosong dalam case saya memiliki panjang 1

Dalam
sumber
2

Jika Anda menggunakan Linux, Anda dapat mencoba pendekatan berikut.
Misalkan Anda memiliki file teks bernama animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Hapus baris pertama:

>>> import subprocess
>>> subprocess.call(['sed','-i','/.*dog.*/d','animal.txt']) 

kemudian

$ cat animal.txt
pig
cat
monkey
elephant
Ren
sumber
7
Solusi ini bukan OS agnostik, dan karena OP tidak menentukan sistem operasi, tidak ada alasan untuk memposting imo jawaban khusus Linux.
Steinar Lima
2
Siapa pun yang menyarankan menggunakan subproses untuk apa pun yang dapat dilakukan hanya dengan python mendapat downvote! Dan +1 ke @SteinarLima ... Saya setuju
Jamie Lindsey
2

Saya pikir jika Anda membaca file ke dalam daftar, maka lakukan itu Anda dapat mengulangi daftar untuk mencari nama panggilan yang ingin Anda singkirkan. Anda dapat melakukannya dengan lebih efisien tanpa membuat file tambahan, tetapi Anda harus menulis hasilnya kembali ke file sumber.

Begini cara saya melakukan ini:

import, os, csv # and other imports you need
nicknames_to_delete = ['Nick', 'Stephen', 'Mark']

Saya berasumsi nicknames.csvmengandung data seperti:

Nick
Maria
James
Chris
Mario
Stephen
Isabella
Ahmed
Julia
Mark
...

Kemudian muat file ke dalam daftar:

 nicknames = None
 with open("nicknames.csv") as sourceFile:
     nicknames = sourceFile.read().splitlines()

Selanjutnya, beralih ke daftar untuk mencocokkan input Anda untuk dihapus:

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")
     except ValueError:
         pass

Terakhir, tulis kembali hasilnya ke file:

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()
A Malik
sumber
1

Secara umum, Anda tidak bisa; Anda harus menulis seluruh file lagi (setidaknya dari titik perubahan sampai akhir).

Dalam beberapa kasus tertentu Anda dapat melakukan lebih baik dari ini -

jika semua elemen data Anda memiliki panjang yang sama dan tanpa urutan tertentu, dan Anda tahu offset yang ingin Anda singkirkan, Anda bisa menyalin item terakhir di atas yang akan dihapus dan memotong file sebelum item terakhir. ;

atau Anda bisa menimpa potongan data dengan nilai 'ini adalah data yang buruk, lewati saja' atau pertahankan tanda 'item ini telah dihapus' di elemen data yang disimpan sehingga Anda dapat menandainya dihapus tanpa memodifikasi file.

Ini mungkin berlebihan untuk dokumen pendek (ada yang di bawah 100 KB?).

Hugh Bothwell
sumber
1

Mungkin, Anda sudah mendapat jawaban yang benar, tetapi ini milik saya. Alih-alih menggunakan daftar untuk mengumpulkan data tanpa filter ( readlines()metode apa ), saya menggunakan dua file. Satu untuk menyimpan data utama, dan yang kedua adalah untuk memfilter data saat Anda menghapus string tertentu. Ini kode:

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' not in line:    # remove a specific string
        main_file.write(line)                # put all strings back to your db except deleted
    else: pass
main_file.close()

Semoga Anda menemukan ini berguna! :)

andrii1986
sumber
0

Simpan baris file dalam daftar, lalu hapus daftar baris yang ingin Anda hapus dan tulis baris yang tersisa ke file baru

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)
Henrique Andrade
sumber
Saat memberikan jawaban, lebih baik memberi penjelasan mengapa MENGAPA jawaban Anda adalah jawabannya .
Stephen Rauch
Jika file Anda tidak berakhir dengan baris baru, kode ini tidak akan menghapus baris terakhir bahkan jika itu mengandung kata yang ingin Anda hapus.
Boris
0

inilah beberapa metode lain untuk menghapus baris / a dari file:

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()
ungalcrys
sumber
0

Saya suka metode ini menggunakan fileinput dan metode 'inplace':

import fileinput
for line in fileinput.input(fname, inplace =1):
    line = line.strip()
    if not 'UnwantedWord' in line:
        print(line)

Ini sedikit kurang bertele-tele daripada jawaban lain dan cukup cepat untuk

Ru887321
sumber
0

Anda bisa menggunakan reperpustakaan

Dengan asumsi bahwa Anda dapat memuat txt-file lengkap Anda. Anda kemudian mendefinisikan daftar nama panggilan yang tidak diinginkan dan kemudian menggantinya dengan string kosong "".

# Delete unwanted characters
import 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)
mrk
sumber
-1

Untuk menghapus baris file tertentu dengan nomor barisnya :

Ganti variabel nama file dan line_to_delete dengan nama file Anda dan nomor baris yang ingin Anda hapus.

filename = 'foo.txt'
line_to_delete = 3
initial_line = 1
file_lines = {}

with open(filename) as f:
    content = f.readlines() 

for line in content:
    file_lines[initial_line] = line.strip()
    initial_line += 1

f = open(filename, "w")
for line_number, line_content in file_lines.items():
    if line_number != line_to_delete:
        f.write('{}\n'.format(line_content))

f.close()
print('Deleted line: {}'.format(line_to_delete))

Contoh output :

Deleted line: 3
Aram Maliachi
sumber
tidak perlu membuat dikte, cukup gunakanfor nb, line in enumerate(f.readlines())
Dionys
-3

Ambil konten file, bagi dengan baris baru menjadi tuple. Kemudian, akses nomor baris tuple Anda, gabungkan tuple hasil Anda, dan timpa file tersebut.

Nikhil
sumber
6
(1) maksudmu tuple(f.read().split('\n'))?? (2) "akses nomor baris tuple Anda" dan "gabungkan tuple hasil Anda" terdengar agak misterius; kode Python yang sebenarnya mungkin lebih dimengerti.
John Machin