Вопрос

Я хочу перебрать содержимое текстового файла, выполнить поиск и замену в некоторых строках и записать результат обратно в файл. Я мог бы сначала загрузить весь файл в память, а затем записать его обратно, но это, вероятно, не лучший способ сделать это.

Как лучше всего это сделать в следующем коде?

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

Что здесь происходит:

<Ол>
  • Исходный файл перемещается в файл резервной копии
  • Стандартный вывод перенаправляется в исходный файл в цикле
  • Таким образом, все операторы print записываются обратно в исходный файл
  • fileinput имеет больше наворотов. Например, его можно использовать для автоматической работы со всеми файлами в sys.args [1:] , без необходимости явно их перебирать. Начиная с Python 3.2, он также предоставляет удобный менеджер контекста для использования в операторе with .

    <Ч>

    Хотя fileinput отлично подходит для одноразовых скриптов, я бы с осторожностью использовал его в реальном коде, потому что, по общему признанию, он не очень читабелен или знаком. В реальном (производственном) коде стоит потратить всего несколько строк кода, чтобы сделать процесс явным и, следовательно, сделать код читабельным.

    Есть два варианта:

    <Ол>
  • Файл не слишком большой, и вы можете просто прочитать его полностью в память. Затем закройте файл, снова откройте его в режиме записи и запишите измененное содержимое обратно.
  • Файл слишком велик для хранения в памяти; Вы можете переместить его во временный файл и открыть его, читая его построчно, записывая обратно в исходный файл. Обратите внимание, что для этого требуется вдвое больше места.
  • Вот еще один пример, который был протестирован и будет соответствовать поиску & amp; заменить шаблоны:

    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!<*>quot;,"Goodbye\sWorld.")
    

    Это должно работать: (редактирование на месте)

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

    На основании ответа Томаса Ватнедала. Тем не менее, это не дает точного ответа на прямую часть исходного вопроса. Функция все еще может заменять построчно

    Эта реализация заменяет содержимое файла без использования временных файлов, в результате чего права доступа к файлам остаются неизменными.

    Также re.sub вместо replace, разрешает замену регулярных выражений вместо простой замены текста.

    Чтение файла в виде одной строки вместо строки за строкой позволяет выполнять многострочное сопоставление и замену.

    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 )
    

    Создайте новый файл, скопируйте строки из старого в новый и выполните замену, прежде чем записывать строки в новый файл.

    Расширяя ответ @ Kiran, который, я согласен, является более лаконичным и 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 в качестве шаблона, я смог найти в файле строку, соответствующую моему регулярному выражению, и заменить ее пустой строкой.

    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)
    

    Для пользователей Linux:

    import os
    os.system('sed -i \'s/foo/bar/\' '+file_path)
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top