จะค้นหาและแทนที่ข้อความในไฟล์ได้อย่างไร?


212

ฉันจะค้นหาและแทนที่ข้อความในไฟล์โดยใช้ Python 3 ได้อย่างไร

นี่คือรหัสของฉัน:

import os
import sys
import fileinput

print ("Text to search for:")
textToSearch = input( "> " )

print ("Text to replace it with:")
textToReplace = input( "> " )

print ("File to perform Search-Replace on:")
fileToSearch  = input( "> " )
#fileToSearch = 'D:\dummy1.txt'

tempFile = open( fileToSearch, 'r+' )

for line in fileinput.input( fileToSearch ):
    if textToSearch in line :
        print('Match Found')
    else:
        print('Match Not Found!!')
    tempFile.write( line.replace( textToSearch, textToReplace ) )
tempFile.close()


input( '\n\n Press Enter to exit...' )

ไฟล์อินพุต:

hi this is abcd hi this is abcd
This is dummy text file.
This is how search and replace works abcd

เมื่อฉันค้นหาและแทนที่ 'ram' โดย 'abcd' ในไฟล์อินพุตด้านบนมันใช้งานได้อย่างมีเสน่ห์ แต่เมื่อฉันทำในทางกลับกันคือแทนที่ 'abcd' ด้วย 'ram' อักขระขยะบางตัวจะถูกทิ้งไว้ที่ท้าย

แทนที่ 'abcd' โดย 'ram'

hi this is ram hi this is ram
This is dummy text file.
This is how search and replace works rambcd

คุณจะมีความเจาะจงมากขึ้นเมื่อคุณพูดว่า "อักขระขยะบางตัวถูกทิ้งไว้ในท้ายที่สุด" คุณเห็นอะไรบ้าง
Burhan Khalid

อัปเดตคำถามด้วยผลลัพธ์ที่ฉันได้รับ
Shriram

คำตอบ:


241

fileinputรองรับการแก้ไข inplace แล้ว มันเปลี่ยนเส้นทางstdoutไปยังไฟล์ในกรณีนี้:

#!/usr/bin/env python3
import fileinput

with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
    for line in file:
        print(line.replace(text_to_search, replacement_text), end='')

13
การend=''โต้แย้งควรจะทำอย่างไร?
egpbos

18
lineมีการขึ้นบรรทัดใหม่แล้ว endเป็นบรรทัดใหม่ตามค่าเริ่มต้นend=''ทำให้print()ฟังก์ชั่นไม่ต้องพิมพ์ newline เพิ่มเติม
jfs

11
อย่าใช้ไฟล์อินพุต! ลองเขียนโค้ดเพื่อทำสิ่งนี้ด้วยตัวเองแทน การเปลี่ยนเส้นทาง sys.stdout ไม่ใช่ความคิดที่ดีโดยเฉพาะอย่างยิ่งถ้าคุณทำโดยไม่ต้องลอง .. หากมีข้อยกเว้นเกิดขึ้น stdout ของคุณอาจไม่ถูกกู้คืน
craigds

9
@craigds: ผิด fileinputไม่ใช่เครื่องมือสำหรับงานทั้งหมด ( ไม่มีอะไรเลย ) แต่มีหลายกรณีที่มันเป็นเครื่องมือที่เหมาะสมเช่นในการติดตั้งsedตัวกรองเหมือนใน Python อย่าใช้ไขควงเพื่อตอกตะปู
jfs

5
หากคุณจริงๆต้องการเปลี่ยนเส้นทาง stdout ไปยังไฟล์ของคุณด้วยเหตุผลบางอย่างก็ไม่ยากที่จะทำมันได้ดีกว่าfileinputไม่ (โดยทั่วไปการใช้งานtry..finallyหรือ ContextManager เพื่อให้แน่ใจว่าคุณตั้งค่ากลับ stdout ไปเป็นค่าเดิมหลังจากนั้น) ซอร์สโค้ดสำหรับfileinputค่อนข้างน่ากลัวและมีบางสิ่งที่ไม่ปลอดภัยภายใต้ประทุน หากมีการเขียนวันนี้ฉันสงสัยมากมันจะทำให้มันเป็น stdlib
craigds

333

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

# Read in the file
with open('file.txt', 'r') as file :
  filedata = file.read()

# Replace the target string
filedata = filedata.replace('ram', 'abcd')

# Write the file out again
with open('file.txt', 'w') as file:
  file.write(filedata)

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


5
with file = open(..):ไม่ถูกต้อง Python ( =) แม้ว่าเจตนาชัดเจน .replace()ไม่ได้แก้ไขสตริง (มันไม่เปลี่ยนรูป) ดังนั้นคุณต้องใช้ค่าที่ส่งคืน อย่างไรก็ตามรหัสที่รองรับไฟล์ขนาดใหญ่นั้นสามารถทำได้ง่ายกว่าเว้นแต่คุณจะต้องค้นหาและแทนที่ข้อความที่ครอบคลุมหลายบรรทัด
jfs

40
คุณพูดถูกและนั่นก็เป็นเหตุผลว่าทำไมคุณควรทดสอบโค้ดของคุณก่อนที่คุณจะเขินอายบนอินเทอร์เน็ต;)
Jack Aidley

19
@ JonasStein: ไม่มันไม่ควร withคำสั่งโดยอัตโนมัติปิดไฟล์ในตอนท้ายของบล็อกคำสั่ง
Jack Aidley

2
@JackAidley ที่น่าสนใจ ขอบคุณสำหรับคำอธิบาย
Jonas Stein

4
@JackAidley เพราะสั้นง่ายใช้งานง่ายและเข้าใจได้ง่ายและจัดการปัญหาที่แท้จริงที่ผู้คนจำนวนมากมี
Ben Barden

52

ดังที่ Jack Aidley โพสต์ไว้และ JF Sebastian ชี้ให้เห็นรหัสนี้จะไม่ทำงาน:

 # Read in the file
filedata = None
with file = open('file.txt', 'r') :
  filedata = file.read()

# Replace the target string
filedata.replace('ram', 'abcd')

# Write the file out again
with file = open('file.txt', 'w') :
  file.write(filedata)`

แต่รหัสนี้จะใช้งานได้ (ฉันได้ทดสอบแล้ว):

f = open(filein,'r')
filedata = f.read()
f.close()

newdata = filedata.replace("old data","new data")

f = open(fileout,'w')
f.write(newdata)
f.close()

ใช้วิธีนี้ filein และ fileout สามารถเป็นไฟล์เดียวกันได้เนื่องจาก Python 3.3 จะเขียนทับไฟล์เมื่อเปิดเพื่อเขียน


9
ฉันเชื่อว่าความแตกต่างอยู่ที่นี่: ฟ้องร้อง.replace ('ram', 'abcd') เปรียบเทียบกับ: newdata = filata.replace ("ข้อมูลเก่า", "ข้อมูลใหม่") ไม่มีอะไรเกี่ยวข้องกับคำสั่ง "with"
Diegomanas

5
1. เหตุผลที่คุณจะลบwith-statement? 2. ตามที่ระบุไว้ในคำตอบของฉันfileinputสามารถทำงานในสถานที่ - มันสามารถแทนที่ข้อมูลในไฟล์เดียวกัน (มันใช้ไฟล์ชั่วคราวภายใน) ข้อแตกต่างคือfileinputไม่จำเป็นต้องโหลดไฟล์ทั้งหมดลงในหน่วยความจำ
jfs

8
เพียงเพื่อช่วยให้ผู้อื่นกลับมาคำตอบของ Jack Aidley มันได้รับการแก้ไขตั้งแต่คำตอบนี้ดังนั้นตอนนี้จึงเหลือเฟือ (และด้อยกว่าเนื่องจากการสูญเสียwithบล็อกผู้ชนะ)
Chris

46

คุณสามารถทำการทดแทนได้เช่นนี้

f1 = open('file1.txt', 'r')
f2 = open('file2.txt', 'w')
for line in f1:
    f2.write(line.replace('old_text', 'new_text'))
f1.close()
f2.close()

7

pathlibนอกจากนี้คุณยังสามารถใช้

from pathlib2 import Path
path = Path(file_to_search)
text = path.read_text()
text = text.replace(text_to_search, replacement_text)
path.write_text(text)

ขอบคุณ Yuya วิธีการแก้ปัญหาข้างต้นทำงานได้ดี หมายเหตุ: คุณต้องสำรองไฟล์ต้นฉบับของคุณก่อนเนื่องจากไฟล์นั้นจะมาแทนที่ไฟล์ดั้งเดิมของคุณเอง หากคุณต้องการแทนที่ข้อความซ้ำ ๆ คุณสามารถเพิ่ม 2 บรรทัดสุดท้ายดังต่อไปนี้ text = text.replace (text_to_search, replace_text) path.write_text (text)
Nages

3

ด้วยบล็อกเดียวคุณสามารถค้นหาและแทนที่ข้อความของคุณ:

with open('file.txt','r+') as f:
    filedata = f.read()
    filedata = filedata.replace('abc','xyz')
    f.truncate(0)
    f.write(filedata)

1
คุณลืมที่seekจะเริ่มต้นไฟล์ก่อนที่จะเขียน truncateไม่ทำอย่างนั้นคุณจะมีขยะในไฟล์
นะ

2

ปัญหาของคุณเกิดจากการอ่านและเขียนไฟล์เดียวกัน แทนที่จะเปิดfileToSearchสำหรับการเขียนเปิดแฟ้มชั่วคราวที่เกิดขึ้นจริงแล้วหลังจากที่คุณทำและมีการปิดtempFileการใช้งานที่จะย้ายไฟล์ใหม่กว่าos.renamefileToSearch


1
FYI ที่เป็นมิตร (อย่าลังเลที่จะแก้ไขในคำตอบ): สาเหตุที่แท้จริงไม่สามารถย่อให้ตรงกลางไฟล์ได้ นั่นคือถ้าคุณค้นหา 5 ตัวอักษรและแทนที่ด้วย 3 ตัวอักษร 3 ตัวแรกของ 5 ตัวที่ค้นหาจะถูกแทนที่ แต่อีก 2 ไม่สามารถลบพวกเขาจะอยู่ที่นั่น โซลูชันไฟล์ชั่วคราวจะลบอักขระ "ที่เหลือ" เหล่านี้โดยวางลงแทนที่จะเขียนลงในไฟล์ชั่วคราว
michaelb958 - GoFundMonica

2

(pip install python-util)

from pyutil import filereplace

filereplace("somefile.txt","abcd","ram")

พารามิเตอร์ที่สอง (สิ่งที่จะถูกแทนที่เช่น "abcd" สามารถเป็น regex ได้)
จะแทนที่สิ่งที่เกิดขึ้นทั้งหมด


ฉันมีประสบการณ์ที่ไม่ดีเกี่ยวกับเรื่องนี้ (มันเพิ่มอักขระบางตัวไว้ที่ส่วนท้ายของไฟล์) ดังนั้นฉันไม่สามารถแนะนำได้
Azrael3000

@ Azrael3000 มันเพิ่มตัวละคร? ฉันไม่เห็นว่าเกิดขึ้นกับฉัน ฉันจะขอบคุณมากถ้าคุณเปิดปัญหากับ Github ดังนั้นฉันสามารถแก้ไขได้github.com/MisterL2/python-util
MisterL2

1

ตัวแปรของฉันหนึ่งคำต่อครั้งในไฟล์ทั้งหมด

ฉันอ่านมันลงในหน่วยความจำ

def replace_word(infile,old_word,new_word):
    if not os.path.isfile(infile):
        print ("Error on replace_word, not a regular file: "+infile)
        sys.exit(1)

    f1=open(infile,'r').read()
    f2=open(infile,'w')
    m=f1.replace(old_word,new_word)
    f2.write(m)

0

ฉันทำสิ่งนี้แล้ว:

#!/usr/bin/env python3

import fileinput
import os

Dir = input ("Source directory: ")
os.chdir(Dir)

Filelist = os.listdir()
print('File list: ',Filelist)

NomeFile = input ("Insert file name: ")

CarOr = input ("Text to search: ")

CarNew = input ("New text: ")

with fileinput.FileInput(NomeFile, inplace=True, backup='.bak') as file:
    for line in file:
        print(line.replace(CarOr, CarNew), end='')

file.close ()

เศร้า แต่fileinput doen ไม่ได้ทำงานร่วมกับกับinplace=True utf-8
Sergio

0

ฉันแก้ไขโพสต์ของ Jayram Singh เล็กน้อยเพื่อแทนที่ 'อินสแตนซ์'! ตัวอักษรเป็นตัวเลขที่ฉันต้องการเพิ่มขึ้นกับแต่ละอินสแตนซ์ คิดว่าอาจเป็นประโยชน์กับคนที่ต้องการแก้ไขอักขระที่เกิดขึ้นมากกว่าหนึ่งครั้งต่อบรรทัดและต้องการย้ำ หวังว่าจะช่วยใครซักคน ป.ล. - ฉันเพิ่งเริ่มเขียนโค้ดใหม่ดังนั้นต้องขออภัยหากโพสต์ของฉันไม่เหมาะสม แต่อย่างใด

f1 = open('file1.txt', 'r')
f2 = open('file2.txt', 'w')
n = 1  

# if word=='!'replace w/ [n] & increment n; else append same word to     
# file2

for line in f1:
    for word in line:
        if word == '!':
            f2.write(word.replace('!', f'[{n}]'))
            n += 1
        else:
            f2.write(word)
f1.close()
f2.close()

0
def word_replace(filename,old,new):
    c=0
    with open(filename,'r+',encoding ='utf-8') as f:
        a=f.read()
        b=a.split()
        for i in range(0,len(b)):
            if b[i]==old:
                c=c+1
        old=old.center(len(old)+2)
        new=new.center(len(new)+2)
        d=a.replace(old,new,c)
        f.truncate(0)
        f.seek(0)
        f.write(d)
    print('All words have been replaced!!!')

รหัสนี้จะแทนที่คำที่คุณต้องการ ปัญหาเดียวก็คือมันเขียนไฟล์ทั้งหมด อาจค้างหากไฟล์ยาวเกินไปสำหรับตัวประมวลผลที่จะจัดการ
Vinit Pillai

0

ชอบมาก

def find_and_replace(file, word, replacement):
  with open(file, 'r+') as f:
    text = f.read()
    f.write(text.replace(word, replacement))

โปรดตรวจสอบให้แน่ใจว่าคำตอบของคุณดีขึ้นเมื่อมีคำตอบอื่นอยู่แล้วในคำถามนี้
hongsy

นี้จะผนวกข้อความที่มีการแทนที่ท้ายไฟล์ในความคิดของฉัน @Jack Aidley aswer เป็นเพียงสิ่งที่ OP หมายถึงstackoverflow.com/a/17141572/6875391
Kirill

-3
def findReplace(find, replace):

    import os 

    src = os.path.join(os.getcwd(), os.pardir) 

    for path, dirs, files in os.walk(os.path.abspath(src)):

        for name in files: 

            if name.endswith('.py'): 

                filepath = os.path.join(path, name)

                with open(filepath) as f: 

                    s = f.read()

                s = s.replace(find, replace) 

                with open(filepath, "w") as f:

                    f.write(s) 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.