سؤال

أريد أن حلقة على محتويات ملف نصي و القيام بعملية بحث واستبدال بعض خطوط كتابة النتيجة مرة أخرى إلى الملف.يمكنني أولا تحميل الملف بأكمله في الذاكرة ثم اكتبها مرة أخرى, ولكن هذا ربما ليست أفضل طريقة للقيام بذلك.

ما هي أفضل طريقة للقيام بذلك ، داخل البرمجية التالية?

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
هل كانت مفيدة؟

المحلول

أظن أن شيئا من هذا القبيل ينبغي أن تفعل ذلك.انها في الأساس يكتب المحتوى إلى ملف جديد و يستبدل الملف القديم مع الملف الجديد:

from tempfile import mkstemp
from shutil import move
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))
    #Remove original file
    remove(file_path)
    #Move new file
    move(abs_path, file_path)

نصائح أخرى

أقصر الطرق ربما يكون استخدام fileinput وحدة.على سبيل المثال التالي يضيف أرقام الأسطر إلى ملف في نفس المكان:

import fileinput

for line in fileinput.input("test.txt", inplace=True):
    print "%d: %s" % (fileinput.filelineno(), line),

ما يحدث هنا هو:

  1. الملف الأصلي يتم نقل ملف النسخ الاحتياطي
  2. الإخراج القياسي هو توجيهك إلى الملف الأصلي ضمن حلقة
  3. وبالتالي أي print البيانات الكتابة مرة أخرى إلى الملف الأصلي

fileinput لديه المزيد من أجراس وصفارات.على سبيل المثال, يمكن استخدامه للعمل تلقائيا على جميع الملفات في sys.args[1:], من دون الحاجة إلى تكرار عليهم صراحة.بدءا من بيثون 3.2 كما يوفر مريحة سياق مدير للاستخدام في with البيان.


في حين fileinput تعتبر كبيرة بالنسبة بخس النصوص ، سأكون حذرا من استخدامه في رمز حقيقي لأن المسلم ليس للقراءة أو مألوفة.في الحقيقية (الإنتاج) رمز إنه من المفيد أن تنفق فقط بضعة أسطر من التعليمات البرمجية إلى إجراء عملية صريحة وبالتالي جعل رمز للقراءة.

هناك اثنين من الخيارات:

  1. الملف ليس كبيرا للغاية ، يمكنك فقط قراءة كليا على الذاكرة.ثم أغلق الملف فتحه في وضع الكتابة وكتابة تعديل محتويات مرة أخرى.
  2. الملف كبير جدا ليتم تخزينها في الذاكرة ؛ يمكنك نقله إلى ملف مؤقت و أن قراءته سطرا سطرا, الكتابة مرة أخرى إلى الملف الأصلي.لاحظ أن هذا يتطلب ضعف التخزين.

هنا مثال آخر أن الاختبارات ستكون مباراة البحث و استبدال أنماط:

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

هذا يجب أن تعمل:(inplace التحرير)

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

استنادا إلى إجابة من قبل توماس Watnedal.ولكن هذا لا يجيب على خط إلى خط جزء من السؤال الأصلي بالضبط.وظيفة لا تزال محل على خط إلى خط أساس

هذا التطبيق يحل محل محتويات الملف دون استخدام الملفات المؤقتة ، ونتيجة لذلك أذونات الملف دون تغيير.

أيضا إعادة.الفرعية بدلا من استبدال يسمح regex استبدال بدلا من النص العادي استبدال فقط.

قراءة ملف سلسلة واحدة بدلا من خط سطرا يسمح متعدد مباراة والاستبدال.

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

كما lassevk يقترح كتابة الملف الجديد كما تذهب هنا بعض التعليمة البرمجية الموجودة في المثال:

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

إذا كنت ترغب في وظيفة عامة أن يستبدل أي النص مع نص آخر ، وهذا هو الأرجح أفضل وسيلة للذهاب ، وبخاصة إذا كنت من محبي من التعابير المنطقية هي:

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 )

أكثر pythonic طريقة استخدام السياق مديري مثل رمز أدناه:

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)

يمكنك العثور على كامل مقتطف هنا.

إنشاء ملف جديد ، نسخ الخطوط من القديم إلى الجديد ، والقيام باستبدال قبل أن تكتب الأسطر إلى ملف جديد.

التوسع في @كيران الجواب الذي أتفق هو أكثر اقتضابا Pythonic ، وهذا يضيف الترميز لدعم القراءة و الكتابة من UTF-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)

باستخدام hamishmcn الجواب كقالب كنت قادرا على البحث عن خطا في الملفات التي تطابق regex واستبدالها سلسلة فارغة.

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

إذا كنت إزالة مسافة بادئة في مثل أدناه ، فإنه سيتم بحث واستبدال في خط متعددة.انظر أدناه على سبيل المثال.

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)

لمستخدمي لينكس:

import os
os.system('sed -i \'s/foo/bar/\' '+file_path)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top