sử dụng Python để xóa một dòng cụ thể trong một tệp


145

Giả sử tôi có một tệp văn bản có đầy đủ các biệt danh. Làm cách nào để xóa biệt hiệu cụ thể khỏi tệp này bằng Python?


1
Hãy thử fileinputtheo mô tả của @ jf-sebastian tại đây . Nó dường như cho phép bạn làm việc từng dòng một, thông qua một tệp tạm thời, tất cả chỉ với một forcú pháp đơn giản .
Kevin

Câu trả lời:


205

Đầu tiên, mở tệp và nhận tất cả các dòng của bạn từ tệp. Sau đó mở lại tệp ở chế độ ghi và ghi lại các dòng của bạn, ngoại trừ dòng bạn muốn xóa:

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)

Bạn cần phải strip("\n")so sánh ký tự dòng mới vì nếu tệp của bạn không kết thúc bằng ký tự dòng mới thì cuối cùng linesẽ không có.


2
Tại sao chúng ta phải mở và đóng nó hai lần?
Ooker

3
@Ooker: Bạn phải mở tệp hai lần (và đóng tệp ở giữa) vì ở chế độ đầu tiên, nó là "chỉ đọc" vì bạn chỉ đọc trong các dòng hiện tại trong tệp. Sau đó, bạn đóng nó và mở lại trong "chế độ ghi", trong đó tệp có thể ghi và bạn thay thế nội dung của tệp sẽ thay đổi dòng bạn muốn xóa.
Devin

4
Tại sao Python không cho phép chúng ta làm điều này trong một dòng?
Ooker

5
@Ooker, Khi bạn đọc một dòng, hãy thử tưởng tượng một con trỏ di chuyển dọc theo dòng khi nó đọc. Khi dòng đó đã được đọc, con trỏ sẽ chạy qua nó. Khi bạn cố gắng ghi vào tệp bạn viết nơi con trỏ hiện đang ở. Bằng cách mở lại tệp bạn đặt lại con trỏ.
Waddas

4
Sử dụng với hợp chất!
Sceluswe

100

Giải pháp cho vấn đề này chỉ với một lần mở duy nhất:

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

Giải pháp này mở tệp ở chế độ r / w ("r +") và sử dụng tìm kiếm để đặt lại con trỏ f sau đó cắt bớt để xóa mọi thứ sau lần ghi cuối cùng.


2
Điều này làm việc rất tốt cho tôi, vì tôi cũng phải sử dụng lockfile (fcntl). Tôi không thể tìm thấy bất kỳ cách nào để sử dụng fileinput cùng với fcntl.
Easyrider

1
Sẽ thật tuyệt khi thấy một số tác dụng phụ của giải pháp này.
dùng1767754

3
Tôi sẽ không làm điều này. Nếu bạn gặp lỗi trong forvòng lặp, bạn sẽ kết thúc với một tệp bị ghi đè một phần, với các dòng trùng lặp hoặc một nửa dòng bị cắt. Bạn có thể muốn f.truncate()ngay sau f.seek(0)đó thay vào đó. Bằng cách đó, nếu bạn gặp lỗi, bạn sẽ kết thúc với một tệp không đầy đủ. Nhưng giải pháp thực sự (nếu bạn có dung lượng đĩa) là xuất ra tệp tạm thời và sau đó sử dụng os.replace()hoặc pathlib.Path(temp_filename).replace(original_filename)trao đổi nó với bản gốc sau khi mọi thứ đã thành công.
Boris

Bạn có thể thêm i.strip('\n') != "line you want to remove..."như đã đề cập trong câu trả lời được chấp nhận, điều đó sẽ giải quyết hoàn hảo vấn đề của tôi. Bởi vì tôi iđã không làm gì cho tôi
Mangohero1

31

Tùy chọn tốt nhất và nhanh nhất, thay vì lưu trữ mọi thứ trong danh sách và mở lại tệp để ghi nó, theo ý kiến ​​của tôi là viết lại tệp ở nơi khác.

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)

Đó là nó! Trong một vòng lặp và chỉ một bạn có thể làm điều tương tự. Nó sẽ nhanh hơn nhiều.


Thay vì sử dụng vòng lặp thông thường, chúng ta có thể sử dụng Trình tạo biểu thức Cách chương trình này sẽ không tải tất cả các dòng từ tệp vào bộ nhớ, đó không phải là ý tưởng tốt trong trường hợp các tệp lớn. Nó sẽ chỉ có một dòng duy nhất trong bộ nhớ tại một thời điểm. Với biểu thức trình tạo cho vòng lặp sẽ trông như thế,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde

4
@ShriShinde Bạn cũng không đọc tệp vào bộ nhớ khi lặp qua đối tượng tệp, vì vậy giải pháp này hoạt động giống với đề xuất của bạn.
Steinar Lima

Bạn có thể muốn xóa tệp gốc và đổi tên tệp thứ hai thành tên tệp gốc, với Python trên hệ điều hành Linux sẽ giống như thế này,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Tối đa

6
os.replace(mới trong python v 3.3) là đa nền tảng hơn so với một cuộc gọi hệ thống mv.
7yl4r

Đơn giản và tuyệt vời.
JuBaer AD

27

Đây là một "ngã ba" từ câu trả lời của @Lother (mà tôi tin rằng nên được coi là câu trả lời đúng).


Đối với một tập tin như thế này:

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

Cái ngã ba từ giải pháp này của Lother hoạt động tốt:

#!/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()

Cải tiến:

  • with open, loại bỏ việc sử dụng f.close()
  • rõ ràng hơn if/elseđể đánh giá nếu chuỗi không có trong dòng hiện tại

Nếu f.seek (0) bắt buộc?
yifan

@yifan vâng. Mặt khác, thay vì ghi đè lên tệp, bạn sẽ nối tệp đó vào chính nó (không có dòng bạn đang loại trừ).
Boris

5

Vấn đề với việc đọc các dòng trong lần đầu tiên và thực hiện các thay đổi (xóa các dòng cụ thể) trong lần chuyển thứ hai là nếu kích thước tệp của bạn rất lớn, bạn sẽ hết RAM. Thay vào đó, một cách tiếp cận tốt hơn là đọc từng dòng một, và viết chúng thành một tệp riêng biệt, loại bỏ những thứ bạn không cần. Tôi đã chạy phương pháp này với các tệp lớn tới 12-50 GB và mức sử dụng RAM gần như không đổi. Chỉ các chu kỳ CPU hiển thị xử lý trong tiến trình.


2

Tôi thích cách tiếp cận fileinput như được giải thích trong câu trả lời này: Xóa một dòng khỏi tệp văn bản (python)

Ví dụ: tôi có một tệp có các dòng trống trong đó và tôi muốn xóa các dòng trống, đây là cách tôi giải quyết nó:

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

Lưu ý: Các dòng trống trong trường hợp của tôi có độ dài 1


2

Nếu bạn sử dụng Linux, bạn có thể thử cách tiếp cận sau.
Giả sử bạn có một tệp văn bản có tên animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Xóa dòng đầu tiên:

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

sau đó

$ cat animal.txt
pig
cat
monkey
elephant

7
Giải pháp này không phải là bất khả tri của hệ điều hành và vì OP không chỉ định một hệ điều hành, không có lý do gì để đăng một imo câu trả lời cụ thể của Linux.
Steinar Lima

2
Bất cứ ai đề xuất sử dụng quy trình con cho bất cứ điều gì có thể được thực hiện chỉ với python đều bị downvote! Và +1 cho @SteinarLima ... Tôi đồng ý
Jamie Lindsey

2

Tôi nghĩ rằng nếu bạn đọc tệp vào một danh sách, thì hãy làm theo cách bạn có thể lặp qua danh sách để tìm tên hiệu bạn muốn loại bỏ. Bạn có thể làm điều đó hiệu quả mà không cần tạo thêm tệp, nhưng bạn sẽ phải ghi lại kết quả vào tệp nguồn.

Đây là cách tôi có thể làm điều này:

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

Tôi giả sử nicknames.csvcó chứa dữ liệu như:

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

Sau đó tải tập tin vào danh sách:

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

Tiếp theo, lặp lại danh sách để khớp với đầu vào của bạn cần xóa:

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

Cuối cùng, viết kết quả trở lại vào tập tin:

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

1

Nói chung, bạn không thể; bạn phải viết lại toàn bộ tập tin (ít nhất là từ điểm thay đổi đến cuối).

Trong một số trường hợp cụ thể, bạn có thể làm tốt hơn thế này -

nếu tất cả các thành phần dữ liệu của bạn có cùng độ dài và không theo thứ tự cụ thể và bạn biết phần bù của phần tử bạn muốn loại bỏ, bạn có thể sao chép mục cuối cùng qua mục cần xóa và cắt bớt tệp trước mục cuối cùng ;

hoặc bạn chỉ có thể ghi đè lên đoạn dữ liệu với 'đây là dữ liệu xấu, bỏ qua giá trị' hoặc giữ cờ 'mục này đã bị xóa' trong các thành phần dữ liệu đã lưu của bạn để bạn có thể đánh dấu nó bị xóa mà không cần sửa đổi tệp.

Đây có thể là quá mức cần thiết cho các tài liệu ngắn (bất cứ điều gì dưới 100 KB?).


1

Có lẽ, bạn đã có một câu trả lời đúng, nhưng đây là của tôi. Thay vì sử dụng danh sách để thu thập dữ liệu chưa được lọc ( readlines()phương pháp nào), tôi sử dụng hai tệp. Một là để giữ dữ liệu chính và thứ hai là để lọc dữ liệu khi bạn xóa một chuỗi cụ thể. Đây là một mã:

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

Hy vọng bạn sẽ tìm thấy điều này hữu ích! :)


0

Lưu các dòng tệp trong danh sách, sau đó xóa danh sách dòng bạn muốn xóa và ghi các dòng còn lại vào một tệp mới

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)


Nếu tệp của bạn không kết thúc bằng một dòng mới, mã này sẽ không xóa dòng cuối cùng ngay cả khi nó chứa một từ bạn muốn xóa.
Boris

0

Dưới đây là một số phương pháp khác để xóa một / một số dòng khỏi tệp:

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

0

Tôi thích phương pháp này bằng cách sử dụng fileinput và phương thức 'inplace':

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

Nó ít nói hơn các câu trả lời khác và đủ nhanh để


0

Bạn có thể sử dụng rethư viện

Giả sử rằng bạn có thể tải tệp txt đầy đủ của mình. Sau đó, bạn xác định danh sách các biệt danh không mong muốn và sau đó thay thế chúng bằng một chuỗi trống "".

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

-1

Để xóa một dòng cụ thể của một tệp theo số dòng của nó :

Thay thế tên tệpline_to_delete bằng tên tệp của bạn và số dòng bạn muốn xóa.

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

Ví dụ đầu ra :

Deleted line: 3

không cần xây dựng một dict, chỉ cần sử dụngfor nb, line in enumerate(f.readlines())
Dionys

-3

Lấy nội dung của tệp, chia nó theo dòng mới thành một tuple. Sau đó, truy cập số dòng của tuple của bạn, tham gia bộ kết quả của bạn và ghi đè lên tệp.


6
(1) ý bạn là sao tuple(f.read().split('\n'))?? (2) "truy cập số dòng của tuple của bạn" và "tham gia tuple kết quả của bạn" nghe có vẻ khá bí ẩn; mã Python thực tế có thể dễ hiểu hơn.
John Machin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.