ใช้ Python สำหรับการลบบรรทัดที่ระบุในไฟล์


145

สมมติว่าฉันมีไฟล์ข้อความที่เต็มไปด้วยชื่อเล่น ฉันจะลบชื่อเล่นเฉพาะจากไฟล์นี้โดยใช้ Python ได้อย่างไร


1
พยายามfileinputตามที่อธิบาย @ JF-เซบาสเตียนที่นี่ ดูเหมือนว่าจะช่วยให้คุณทำงานแบบบรรทัดต่อบรรทัดผ่านไฟล์ชั่วคราวทั้งหมดด้วยforไวยากรณ์อย่างง่าย
เควิน

คำตอบ:


205

ก่อนอื่นให้เปิดไฟล์และรับทุกบรรทัดของคุณจากไฟล์ จากนั้นเปิดไฟล์อีกครั้งในโหมดเขียนและเขียนบรรทัดของคุณกลับยกเว้นบรรทัดที่คุณต้องการลบ:

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)

คุณต้องstrip("\n")ใช้อักขระขึ้นบรรทัดใหม่ในการเปรียบเทียบเพราะหากไฟล์ของคุณไม่ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่ตัวสุดท้ายlineจะไม่เหมือนกัน


2
ทำไมเราต้องเปิดและปิดสองครั้ง
Ooker

3
@Ooker: คุณต้องเปิดไฟล์สองครั้ง (และปิดในระหว่าง) เพราะในโหมดแรกมันเป็น "อ่านอย่างเดียว" เพราะคุณเพิ่งอ่านในบรรทัดปัจจุบันในไฟล์ จากนั้นคุณปิดและเปิดใหม่ใน "โหมดการเขียน" ซึ่งไฟล์สามารถเขียนได้และคุณแทนที่เนื้อหาของไฟล์นั้นจะเป็นบรรทัดที่คุณต้องการลบ
Devin

4
ทำไมไพ ธ อนไม่อนุญาตให้เราทำสิ่งนี้ในหนึ่งบรรทัด?
Ooker

5
@Ooker เมื่อคุณอ่านบรรทัดให้ลองจินตนาการว่าเคอร์เซอร์เคลื่อนที่ไปตามบรรทัดขณะที่อ่าน เมื่อบรรทัดนั้นถูกอ่านเคอร์เซอร์จะผ่านไปแล้ว เมื่อคุณพยายามที่จะเขียนลงในไฟล์ที่คุณเขียนเคอร์เซอร์อยู่ที่ไหนในปัจจุบัน เมื่อเปิดไฟล์อีกครั้งคุณจะรีเซ็ตเคอร์เซอร์
Waddas

4
ใช้กับสารประกอบ!
Sceluswe

101

วิธีแก้ไขปัญหานี้โดยเปิดเพียงครั้งเดียว:

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

วิธีการแก้ปัญหานี้จะเปิดไฟล์ในโหมด r / w ("r +") และใช้การค้นหาเพื่อรีเซ็ตตัวชี้ f จากนั้นตัดปลายเพื่อลบทุกอย่างหลังจากการเขียนครั้งล่าสุด


2
มันใช้งานได้ดีมากสำหรับฉันเพราะฉันต้องใช้ lockfile ด้วย (fcntl) ฉันไม่สามารถหาวิธีที่จะใช้ไฟล์ข้อมูลร่วมกับ fcntl
Easyrider

1
มันจะดีที่ได้เห็นผลข้างเคียงของการแก้ปัญหานี้
user1767754

3
ฉันจะไม่ทำสิ่งนี้ หากคุณพบข้อผิดพลาดในforลูปคุณจะพบไฟล์ที่ถูกเขียนทับบางส่วนโดยมีบรรทัดที่ซ้ำกันหรือตัดครึ่งบรรทัด คุณอาจต้องการf.truncate()หลังจากf.seek(0)แทน ด้วยวิธีนี้หากคุณได้รับข้อผิดพลาดคุณจะจบลงด้วยไฟล์ที่ไม่สมบูรณ์ แต่ทางออกที่แท้จริง (ถ้าคุณมีพื้นที่ว่างในดิสก์) คือการส่งออกไปยังไฟล์ชั่วคราวจากนั้นใช้os.replace()หรือpathlib.Path(temp_filename).replace(original_filename)เพื่อสลับกับต้นฉบับหลังจากทุกอย่างสำเร็จ
บอริส

คุณอาจเพิ่มi.strip('\n') != "line you want to remove..."ดังกล่าวในคำตอบที่ยอมรับว่าจะแก้ปัญหาของฉันอย่างสมบูรณ์ เพราะiไม่ได้ทำอะไรเพื่อฉันเลย
Mangohero1

32

ตัวเลือกที่ดีที่สุดและเร็วที่สุดแทนที่จะเก็บทุกอย่างไว้ในรายการและเปิดไฟล์ใหม่เพื่อเขียนมันเป็นความคิดของฉันที่จะเขียนไฟล์ใหม่ที่อื่น

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)

แค่นั้นแหละ! ในวงเดียวและวงเดียวคุณก็สามารถทำสิ่งเดียวกันได้ มันจะเร็วขึ้นมาก


แทนที่จะใช้แบบปกติสำหรับลูปเราสามารถใช้Generator Expressionวิธีนี้โปรแกรมจะไม่โหลดทุกบรรทัดจากไฟล์ไปยังหน่วยความจำซึ่งไม่ใช่ความคิดที่ดีในกรณีของไฟล์ขนาดใหญ่ มันจะมีหน่วยความจำทีละบรรทัดเท่านั้น ด้วยนิพจน์ตัวสร้างของลูปจะมีลักษณะดังนี้(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde

4
@ShriShinde คุณไม่ได้อ่านไฟล์ลงในหน่วยความจำเมื่อวนลูปมากกว่าวัตถุไฟล์ดังนั้นวิธีนี้จะทำงานเหมือนกับคำแนะนำของคุณ
Steinar Lima

คุณอาจต้องการลบไฟล์ต้นฉบับและเปลี่ยนชื่อไฟล์ที่สองเป็นชื่อไฟล์ต้นฉบับซึ่ง Python บน Linux OS จะมีลักษณะเช่นนี้subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max

6
os.replace(ใหม่ในหลาม V 3.3) mvมีมากขึ้นข้ามแพลตฟอร์มกว่าการโทรระบบ
7yl4r

เรียบง่ายและยอดเยี่ยม
JuBaer AD

27

นี่คือ "ทางแยก" จากคำตอบของ@Lother (ซึ่งฉันเชื่อว่าควรเป็นคำตอบที่ถูกต้อง)


สำหรับไฟล์เช่นนี้:

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

ทางแยกจากโซลูชันของ Lother นี้ใช้ได้ผลดี:

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

การปรับปรุง:

  • with openซึ่งยกเลิกการใช้งานของ f.close()
  • ชัดเจนยิ่งขึ้นif/elseสำหรับการประเมินว่าสตริงไม่ได้อยู่ในบรรทัดปัจจุบัน

ถ้าจำเป็นต้องใช้ f.seek (0)
yifan

@ yifan ใช่ มิฉะนั้นแทนที่จะเขียนทับไฟล์คุณจะต่อท้ายไฟล์ด้วยตัวเอง (โดยไม่ต้องใส่บรรทัดที่คุณยกเว้น)
บอริส

5

ปัญหาเกี่ยวกับการอ่านบรรทัดในการผ่านครั้งแรกและการเปลี่ยนแปลง (การลบบรรทัดที่ระบุ) ในรอบที่สองคือถ้าคุณขนาดไฟล์มีขนาดใหญ่คุณจะหมด RAM วิธีที่ดีกว่าคือการอ่านบรรทัดทีละบรรทัดแล้วเขียนลงในไฟล์แยกต่างหากกำจัดสิ่งที่คุณไม่ต้องการ ฉันใช้วิธีการนี้กับไฟล์ที่มีขนาดใหญ่ถึง 12-50 GB และการใช้ RAM ยังคงไม่เปลี่ยนแปลง รอบการทำงานของ CPU เท่านั้นที่แสดงการดำเนินการ


2

ฉันชอบวิธีป้อนไฟล์ตามที่อธิบายในคำตอบนี้: การ ลบบรรทัดออกจากไฟล์ข้อความ (ไพ ธ อน)

พูดเช่นฉันมีไฟล์ที่มีบรรทัดว่างอยู่ในนั้นและฉันต้องการลบบรรทัดว่างนี่คือวิธีที่ฉันแก้ไขมัน:

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

หมายเหตุ: บรรทัดว่างในกรณีของฉันมีความยาว 1


2

หากคุณใช้ Linux คุณสามารถลองวิธีการต่อไปนี้
สมมติว่าคุณมีไฟล์ข้อความชื่อanimal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

ลบบรรทัดแรก:

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

แล้วก็

$ cat animal.txt
pig
cat
monkey
elephant

7
วิธีนี้ไม่ได้เป็นระบบปฏิบัติการที่ไม่เชื่อเรื่องพระเจ้าและเนื่องจาก OP ไม่ได้ระบุระบบปฏิบัติการจึงไม่มีเหตุผลที่จะโพสต์คำตอบเฉพาะสำหรับ Linux
Steinar Lima

2
ทุกคนที่แนะนำให้ใช้ subprocess เพื่ออะไรก็ตามที่สามารถทำได้ด้วย python เพียงอย่างเดียวก็จะได้รับการโหวต! และ +1 ถึง @SteinarLima ... ฉันเห็นด้วย
Jamie Lindsey

2

ฉันคิดว่าถ้าคุณอ่านไฟล์ลงในรายการคุณสามารถทำซ้ำในรายการเพื่อค้นหาชื่อเล่นที่คุณต้องการกำจัด คุณสามารถทำได้อย่างมีประสิทธิภาพโดยไม่ต้องสร้างไฟล์เพิ่มเติม แต่คุณจะต้องเขียนผลลัพธ์กลับไปที่ไฟล์ต้นฉบับ

นี่คือวิธีที่ฉันอาจทำสิ่งนี้:

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

ฉันสมมติว่าnicknames.csvมีข้อมูลเช่น:

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

จากนั้นโหลดไฟล์ลงในรายการ:

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

ถัดไปทำซ้ำในรายการเพื่อจับคู่อินพุตของคุณเพื่อลบ:

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

สุดท้ายเขียนผลลัพธ์กลับไปเป็นไฟล์:

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

โดยทั่วไปแล้วคุณไม่สามารถ; คุณต้องเขียนไฟล์ทั้งหมดอีกครั้ง (อย่างน้อยจากจุดเปลี่ยนไปยังจุดสิ้นสุด)

ในบางกรณีคุณสามารถทำได้ดีกว่านี้ -

หากองค์ประกอบข้อมูลทั้งหมดของคุณมีความยาวเท่ากันและไม่มีคำสั่งซื้อที่เฉพาะเจาะจงและคุณรู้ว่าออฟเซ็ตของสิ่งที่คุณต้องการกำจัดคุณสามารถคัดลอกรายการสุดท้ายเหนือรายการที่จะถูกลบและตัดทอนไฟล์ก่อนรายการสุดท้าย ;

หรือคุณสามารถเขียนทับกลุ่มข้อมูลด้วยค่า 'นี่คือข้อมูลที่ไม่ดีข้ามมัน' หรือเก็บค่าสถานะ 'รายการนี้ถูกลบ' ในองค์ประกอบข้อมูลที่บันทึกไว้ของคุณเพื่อให้คุณสามารถทำเครื่องหมายว่าถูกลบโดยไม่ต้องแก้ไขไฟล์

อาจเป็นเพราะเอกสารสั้นเกินไป (มีขนาดต่ำกว่า 100 KB)


1

อาจเป็นไปได้ว่าคุณได้รับคำตอบที่ถูกต้อง แต่นี่คือของฉัน แทนที่จะใช้รายการเพื่อรวบรวมข้อมูลที่ไม่มีการกรอง ( readlines()วิธีใดบ้าง) ฉันใช้สองไฟล์ อันที่หนึ่งใช้สำหรับเก็บข้อมูลหลักและข้อที่สองใช้สำหรับการกรองข้อมูลเมื่อคุณลบสตริงที่ระบุ นี่คือรหัส:

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

หวังว่าคุณจะพบว่ามีประโยชน์นี้! :)


0

บันทึกบรรทัดไฟล์ในรายการจากนั้นลบรายการบรรทัดที่คุณต้องการลบและเขียนบรรทัดที่เหลือลงในไฟล์ใหม่

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)

เมื่อให้คำตอบดีกว่าให้คำอธิบายว่าทำไมคำตอบของคุณจึงเป็นคำตอบ
Stephen Rauch

หากไฟล์ของคุณไม่ได้ขึ้นบรรทัดใหม่รหัสนี้จะไม่ลบบรรทัดสุดท้ายแม้ว่าจะมีคำที่คุณต้องการลบ
บอริส

0

นี่คือวิธีอื่นในการลบ / บางบรรทัดออกจากไฟล์:

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

ฉันชอบวิธีนี้ใช้ fileinput และวิธีการ 'inplace':

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

มันพูดน้อยกว่าคำตอบอื่น ๆ เล็กน้อยและเร็วพอสำหรับ


0

คุณสามารถใช้reห้องสมุด

สมมติว่าคุณสามารถโหลดไฟล์ txt เต็มของคุณได้ จากนั้นคุณจะกำหนดรายการชื่อเล่นที่ไม่ต้องการแล้วแทนที่ด้วยสตริงว่าง ""

# 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

หากต้องการลบบรรทัดของไฟล์ด้วยหมายเลขบรรทัด :

แทนที่ชื่อไฟล์ตัวแปรและline_to_deleteด้วยชื่อไฟล์ของคุณและหมายเลขบรรทัดที่คุณต้องการลบ

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

ตัวอย่างผลลัพธ์ :

Deleted line: 3

ไม่จำเป็นต้องสร้าง dict แค่ใช้for nb, line in enumerate(f.readlines())
Dionys

-3

นำเนื้อหาของไฟล์แยกโดยการขึ้นบรรทัดใหม่เป็น tuple จากนั้นเข้าถึงหมายเลขบรรทัดของ tuple ของคุณเข้าร่วม tuple ผลลัพธ์ของคุณและเขียนทับลงในไฟล์


6
(1) คุณหมายถึงtuple(f.read().split('\n'))?? (2) "เข้าถึงหมายเลขบรรทัดของ tuple" และ "เข้าร่วม tuple ผลลัพธ์ของคุณ" เสียงค่อนข้างลึกลับ รหัส Python จริงอาจเข้าใจได้มากกว่า
John Machin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.