পাইথনের একটি ফাইলে একটি লাইন অনুসন্ধান এবং প্রতিস্থাপন করুন


292

আমি একটি পাঠ্য ফাইলের বিষয়বস্তুগুলি লুপ করতে এবং অনুসন্ধান করতে এবং কিছু লাইনে প্রতিস্থাপন করতে চাই এবং ফলাফলটি ফাইলে ফিরে লিখতে চাই। আমি প্রথমে পুরো ফাইলটি মেমরিতে লোড করতে পারতাম এবং পরে এটি আবার লিখতে পারি, তবে এটি সম্ভবত এটি করার সর্বোত্তম উপায় নয়।

নিম্নলিখিত কোডের মধ্যে এটি করার সর্বোত্তম উপায় কী?

f = open(file)
for line in f:
    if line.contains('foo'):
        newline = line.replace('foo', 'bar')
        # how to write this newline back to the file

উত্তর:


191

আমি অনুমান করি এরকম কিছু করা উচিত। এটি মূলত কন্টেন্টটি একটি নতুন ফাইলে লিখে এবং পুরানো ফাইলটিকে নতুন ফাইলের সাথে প্রতিস্থাপন করে:

from tempfile import mkstemp
from shutil import move, copymode
from os import fdopen, remove

def replace(file_path, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    with fdopen(fh,'w') as new_file:
        with open(file_path) as old_file:
            for line in old_file:
                new_file.write(line.replace(pattern, subst))
    #Copy the file permissions from the old file to the new file
    copymode(file_path, abs_path)
    #Remove original file
    remove(file_path)
    #Move new file
    move(abs_path, file_path)

5
কেবল একটি সামান্য মন্তব্য: fileএকই নামের পূর্বনির্ধারিত শ্রেণীর ছায়া দিচ্ছে।
ইজদাউজেন

4
এই কোডটি মূল ফাইলটিতে অনুমতিগুলি পরিবর্তন করে। আমি কীভাবে মূল অনুমতিগুলি রাখতে পারি?
নিক

1
এফএইচ এর বিন্দুটি কী, আপনি এটি নিকট কলটিতে ব্যবহার করেন তবে কেবল এটি বন্ধ করার জন্য কোনও ফাইল তৈরি করার বিন্দু আমি দেখতে পাচ্ছি না ...
উইসেলো

2
@ উইসেলো ফাইল বিবরণকারী ফাঁস রোধ করতে আপনার এটি বন্ধ করতে হবে। এখানে একটি শালীন ব্যাখ্যা: logilab.org/17873
টমাস ওয়াটনেডাল

1
হ্যাঁ আমি আবিষ্কার করেছি যে mkstemp()এটি একটি 2-টিউপল ফিরিয়ে দিচ্ছে এবং (fh, abs_path) = fh, abs_path, আমি কখনই প্রশ্ন জিজ্ঞাসা করতাম তা জানতাম না।
উইসেলো

271

সবচেয়ে সংক্ষিপ্ততম উপায়টি সম্ভবত ফাইল ইনপুট মডিউলটি ব্যবহার করা হবে । উদাহরণস্বরূপ, নিম্নলিখিতটিতে একটি ফাইলে লাইন নম্বর যুক্ত করা হয়েছে:

import fileinput

for line in fileinput.input("test.txt", inplace=True):
    print('{} {}'.format(fileinput.filelineno(), line), end='') # for Python 3
    # print "%d: %s" % (fileinput.filelineno(), line), # for Python 2

এখানে যা ঘটে তা হ'ল:

  1. আসল ফাইলটি একটি ব্যাকআপ ফাইলে সরানো হয়েছে
  2. স্ট্যান্ডার্ড আউটপুটটি লুপের মধ্যে মূল ফাইলে পুনঃনির্দেশিত হয়
  3. সুতরাং কোনও printবিবৃতি আসল ফাইলটিতে ফিরে লিখুন

fileinputআরও ঘণ্টা এবং হুইসেল রয়েছে উদাহরণস্বরূপ, এটি sys.args[1:]স্পষ্টভাবে আপনার পুনরাবৃত্তি না করেই সমস্ত ফাইলগুলিতে স্বয়ংক্রিয়ভাবে পরিচালনা করতে ব্যবহার করা যেতে পারে be পাইথন ৩.২ দিয়ে শুরু করে এটি withবিবৃতিতে ব্যবহারের জন্য একটি সুবিধাজনক প্রসঙ্গ পরিচালক সরবরাহ করে ।


যদিও fileinputথ্রোওয়ে স্ক্রিপ্টগুলির জন্য দুর্দান্ত, তবে আমি এটি বাস্তব কোডে ব্যবহার করার বিষয়ে সতর্ক থাকব কারণ স্বীকার্যভাবে এটি খুব পাঠযোগ্য বা পরিচিত নয়। প্রকৃত (উত্পাদন) কোডে প্রক্রিয়াটি সুস্পষ্ট করতে এবং কোডটি পাঠযোগ্যযোগ্য করে তুলতে কোডের আরও কয়েকটি লাইন ব্যয় করা সার্থক।

দুটি বিকল্প রয়েছে:

  1. ফাইল অত্যধিক বড় নয় এবং আপনি কেবল এটি পুরোপুরি স্মৃতিতে পড়তে পারেন। তারপরে ফাইলটি বন্ধ করুন, লিখিত মোডে এটি আবার খুলুন এবং পরিবর্তিত সামগ্রীগুলি আবার লিখুন।
  2. ফাইলটি মেমোরিতে সঞ্চয় করার জন্য খুব বড়; আপনি এটিকে একটি অস্থায়ী ফাইলে নিয়ে যেতে পারেন এবং এটি খুলতে পারেন, এটি লাইন দিয়ে লাইন পড়ে, মূল ফাইলে আবার লিখে। মনে রাখবেন যে এটির জন্য দ্বিগুণ সঞ্চয়স্থান প্রয়োজন।

13
আমি জানি এটিতে কেবল এটি দুটি লাইন রয়েছে তবে আমি মনে করি না কোডটি নিজের মধ্যে খুব প্রকাশযুক্ত। কারণ আপনি যদি এক সেকেন্ডের জন্য চিন্তা করেন, যদি আপনি ফাংশনটি না জানতেন তবে কী চলছে তা নিয়ে খুব কম সংকেত রয়েছে। লাইন নম্বর এবং লাইনটি মুদ্রণ করা এটি লেখার মতো নয় ... আপনি যদি আমার
বক্তব্যটি

14
এই শুরু করে ফাইলের মধ্যে লিখুন। এটি স্টডআউট ফাইলে পুনঃনির্দেশ করে। কটাক্ষপাত আছে ডক্স
Brice

32
এখানে মূল কীটি হ'ল মুদ্রণ বিবৃতিটির শেষে কমা: এটি অন্য একটি নতুন লাইন যুক্ত করে মুদ্রণ বিবৃতিকে ছাড়িয়ে যায় (যেমন লাইনের ইতিমধ্যে একটি রয়েছে)। এটি একেবারেই সুস্পষ্ট নয়, যদিও (পাইথন 3 ভাগ্যক্রমে যথেষ্ট যে সিনট্যাক্সটি পরিবর্তন করেছিল)।
ভিপিয়ারিক

4
আপনি যখন ফাইলটিতে একটি খোলার হুক সরবরাহ করেন তখন দয়া করে লক্ষ্য করুন যে আপনি যখন ইউটিএফ -16 এনকোডযুক্ত ফাইলগুলি পড়ার / লেখার চেষ্টা করছেন।
bompf

5
অজগর 3 এর জন্যprint(line, end='')
সিএডিয়া

80

এখানে আরও একটি উদাহরণ যা পরীক্ষা করা হয়েছিল এবং এটি অনুসন্ধানের সাথে মিলবে এবং নিদর্শনগুলি প্রতিস্থাপন করবে:

import fileinput
import sys

def replaceAll(file,searchExp,replaceExp):
    for line in fileinput.input(file, inplace=1):
        if searchExp in line:
            line = line.replace(searchExp,replaceExp)
        sys.stdout.write(line)

উদাহরণ ব্যবহার:

replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.")

23
উদাহরণস্বরূপ ব্যবহারটি একটি নিয়মিত অভিব্যক্তি সরবরাহ করে তবে নিয়মিত প্রকাশের searchExp in lineকাজও নয় line.replace। অবশ্যই উদাহরণ ব্যবহার ভুল।
কোজিরো

আপনি পরিবর্তে if searchExp in line: line = line.replace(searchExp, replaceExpr)শুধু লিখতে পারেন line = line.replace(searchExp, replaceExpr)। কোনও ব্যতিক্রম উত্পন্ন হয় না, লাইনটি কেবল অপরিবর্তিত থাকে।
ডেভিড ওয়ালেস

পাশাপাশি আমার জন্য নিখুঁতভাবে কাজ করেছেন। আমি বেশ কয়েকটি অন্যান্য উদাহরণ দেখতে পেয়েছি যা এর সাথে খুব মিল দেখায় তবে কৌতুকটি ব্যবহারটি ছিল sys.stdout.write(line)। আবার ধন্যবাদ!
সেজে

আমি যদি এটি ব্যবহার করি তবে আমার ফাইলটি ফাঁকা হয়ে যায়। কোন ধারণা?
জাভিয়ের লোপেজ

আমি এটি ব্যবহার করছি
রাকিব ফিহা

64

এটি কাজ করা উচিত: (অন্তর্ভুক্ত সম্পাদনা)

import fileinput

# Does a list of files, and
# redirects STDOUT to the file in question
for line in fileinput.input(files, inplace = 1): 
      print line.replace("foo", "bar"),

5
+1 টি। আপনি যদি একটি RuntimeError গ্রহণ এছাড়াও যদি: ইনপুট () ইতিমধ্যেই সক্রিয় তারপর fileinput.close কল ()
geographika

1
দ্রষ্টব্য যে filesফাইলের নাম যুক্ত একটি স্ট্রিং হওয়া উচিত, কোনও ফাইল অবজেক্ট নয়
atomh33ls

9
মুদ্রণ একটি নতুন লাইন যুক্ত করেছে যা ইতিমধ্যে সেখানে থাকতে পারে। এটি এড়াতে, () আপনার প্রতিস্থাপন শেষে .rstrip যোগ
Guillaume, Gendre

ইনপুট () এর মধ্যে ফাইলগুলি আরগ ব্যবহার করুন, এটি ফাইলিনপুট.ইনপুট (ইনপ্লেস = 1) হতে পারে এবং স্ক্রিপ্টটি> অজগর প্রতিস্থাপন.পি মাইফিলস * .txt
চেস্পিনোজা

24

টমাস ওয়াটনেডালের উত্তরের ভিত্তিতে। তবে এটি মূল প্রশ্নের লাইন-টু-লাইন অংশটির সঠিক উত্তর দেয় না। ফাংশনটি এখনও লাইন থেকে লাইনের ভিত্তিতে প্রতিস্থাপন করতে পারে

এই প্রয়োগটি অস্থায়ী ফাইলগুলি ব্যবহার না করেই ফাইল সামগ্রীগুলি প্রতিস্থাপন করে, ফলস্বরূপ ফাইলের অনুমতিগুলি অপরিবর্তিত থাকে।

এছাড়াও প্রতিস্থাপনের পরিবর্তে re.sub কেবল প্লেইন পাঠ্য প্রতিস্থাপনের পরিবর্তে রিজেক্স প্রতিস্থাপনের অনুমতি দেয়।

লাইন বাই লাইনের পরিবর্তে একক স্ট্রিং হিসাবে ফাইলটি পড়া মাল্টিলাইন ম্যাচ এবং প্রতিস্থাপনের অনুমতি দেয়।

import re

def replace(file, pattern, subst):
    # Read contents from file as a single string
    file_handle = open(file, 'r')
    file_string = file_handle.read()
    file_handle.close()

    # Use RE package to allow for replacement (also allowing for (multiline) REGEX)
    file_string = (re.sub(pattern, subst, file_string))

    # Write contents to file.
    # Using mode 'w' truncates the file.
    file_handle = open(file, 'w')
    file_handle.write(file_string)
    file_handle.close()

2
ফাইলগুলি খোলার সময় আপনি ব্যবহার করতে rbএবং wbবৈশিষ্ট্যগুলি ব্যবহার করতে চাইতে পারেন কারণ এটি আসল লাইনের শেষ সংরক্ষণ করবে
নাক্স

পাইথন 3 এ, আপনি 'ডাব্লুবি' এবং 'আরবি' 'পুনরায়' ব্যবহার করতে পারবেন না। এটি ত্রুটিটি দেবে "

15

লাসসেভেকের পরামর্শ অনুসারে, নতুন ফাইলটি আপনি যাবার সাথে সাথে লিখুন, এখানে কয়েকটি উদাহরণ কোড রয়েছে:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()

12

যদি আপনি একটি জেনেরিক ফাংশন চান যা কোনও পাঠ্যকে অন্য কোনও পাঠ্যের সাথে প্রতিস্থাপন করে , তবে সম্ভবত এটি যাওয়ার সর্বোত্তম উপায়, বিশেষত যদি আপনি রেজেক্সের ভক্ত হন:

import re
def replace( filePath, text, subs, flags=0 ):
    with open( filePath, "r+" ) as file:
        fileContents = file.read()
        textPattern = re.compile( re.escape( text ), flags )
        fileContents = textPattern.sub( subs, fileContents )
        file.seek( 0 )
        file.truncate()
        file.write( fileContents )

12

নীচের কোডের মতো প্রসঙ্গ পরিচালকগণকে ব্যবহার করা আরও একটি অজগর উপায়:

from tempfile import mkstemp
from shutil import move
from os import remove

def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()
    with open(target_file_path, 'w') as target_file:
        with open(source_file_path, 'r') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

আপনি এখানে পুরো স্নিপেট খুঁজে পেতে পারেন ।


পাইথন> = 3.1 এ আপনি দুটি লাইনে কনটেক্সট ম্যানেজার খুলতে পারেন ।
ফ্লোরিসা

4

একটি নতুন ফাইল তৈরি করুন, পুরানো থেকে নতুনটিতে লাইনগুলি অনুলিপি করুন এবং নতুন ফাইলটিতে লাইনগুলি লেখার আগে প্রতিস্থাপনটি করুন।


4

@ কিরণের জবাব, যা আমি সম্মত তা আরও সুসংহত এবং পাইথোনিককে সম্প্রসারণ করা, এটি ইউটিএফ -8 পড়তে এবং লেখাকে সমর্থন করার জন্য কোডকে যুক্ত করেছে:

import codecs 

from tempfile import mkstemp
from shutil import move
from os import remove


def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()

    with codecs.open(target_file_path, 'w', 'utf-8') as target_file:
        with codecs.open(source_file_path, 'r', 'utf-8') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

এটি কি নতুন ফাইলটিতে পুরানো ফাইলের অনুমতি সংরক্ষণ করতে চলেছে?
বিদ্যুৎ

2

হ্যামিশ্মন এর উত্তরটিকে একটি টেম্পলেট হিসাবে ব্যবহার করে আমি একটি ফাইলের একটি লাইন অনুসন্ধান করতে সক্ষম হয়েছি যা আমার রেজেক্সের সাথে মেলে এবং খালি স্ট্রিংয়ের সাথে এটি প্রতিস্থাপন করে।

import re 

fin = open("in.txt", 'r') # in file
fout = open("out.txt", 'w') # out file
for line in fin:
    p = re.compile('[-][0-9]*[.][0-9]*[,]|[-][0-9]*[,]') # pattern
    newline = p.sub('',line) # replace matching strings with empty string
    print newline
    fout.write(newline)
fin.close()
fout.close()

1
আপনার ল্যাপের জন্য রেজেক্সটি আউটসাইড করা উচিত, অন্যথায় একটি পারফরম্যান্স নষ্ট
অ্যাক্সেল

2

fileinput পূর্ববর্তী উত্তরে উল্লিখিত হিসাবে বেশ সরল:

import fileinput

def replace_in_file(file_path, search_text, new_text):
    with fileinput.input(file_path, inplace=True) as f:
        for line in f:
            new_line = line.replace(search_text, new_text)
            print(new_line, end='')

ব্যাখ্যা:

  • fileinputএকাধিক ফাইল গ্রহণ করতে পারে, তবে আমি প্রতিটি একক ফাইলটি প্রক্রিয়া হওয়ার সাথে সাথেই বন্ধ করতে পছন্দ করি। তাই একক স্থাপন file_pathমধ্যে withবিবৃতি।
  • printবিবৃতি কিছু মুদ্রণ করে না inplace=True, কারণ STDOUTমূল ফাইলটিতে ফরোয়ার্ড করা হচ্ছে।
  • end=''মধ্যে printবিবৃতি অন্তর্বর্তী ফাঁকা নতুন লাইন নিষ্কাশন করা হয়।

নিম্নলিখিত হিসাবে ব্যবহার করা যেতে পারে:

file_path = '/path/to/my/file'
replace_in_file(file_path, 'old-text', 'new-text')

0

যদি আপনি নীচের মত ইন্ডেন্টটি সরিয়ে ফেলেন তবে এটি একাধিক লাইনে অনুসন্ধান এবং প্রতিস্থাপন করবে। উদাহরণস্বরূপ নীচে দেখুন।

def replace(file, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    print fh, abs_path
    new_file = open(abs_path,'w')
    old_file = open(file)
    for line in old_file:
        new_file.write(line.replace(pattern, subst))
    #close temp file
    new_file.close()
    close(fh)
    old_file.close()
    #Remove original file
    remove(file)
    #Move new file
    move(abs_path, file)

এই পাইথন কোডটির ফর্ম্যাটটি বেশ সঠিক দেখাচ্ছে না ... (আমি ঠিক করার চেষ্টা করেছি, তবে কী উদ্দেশ্য ছিল তা নিশ্চিত ছিল না)
অ্যান্ডি হেডেন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.