Замена строки в уже открытом файле python [duplicate]

Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение NullReferenceException.

Пример:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

Вы можно просто избежать этого, проверив, является ли переменная не нулевой:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

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

Итак, если вы имеете дело со типами значений, NullReferenceExceptions не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!

Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.

Типы ссылок (эти должны быть проверены):

  • динамический
  • объект
  • string

Типы значений (вы можете просто игнорировать эти):

  • Числовые типы
  • Интегральные типы
  • Типы с плавающей запятой
  • decimal
  • bool
  • Пользовательские структуры

231
задан SilentGhost 7 September 2009 в 11:11
поделиться

13 ответов

Я думаю, что-то вроде этого должно это сделать. Он в основном записывает содержимое в новый файл и заменяет старый файл новым файлом:

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)
155
ответ дан Alex S 26 August 2018 в 14:25
поделиться
  • 1
    Только незначительный комментарий: file затеняет предопределенный класс с тем же именем. – ezdazuzena 24 January 2013 в 17:24
  • 2
    Этот код изменяет права на исходный файл. Как сохранить исходные разрешения? – nic 18 July 2013 в 22:35
  • 3
    в чем смысл fh, вы используете его в закрытом вызове, но я не вижу смысла создавать файл, чтобы его закрыть ... – Wicelo 12 September 2014 в 07:24
  • 4
    @Wicelo Вам нужно закрыть его, чтобы предотвратить утечку дескриптора файла. Вот достойное объяснение: logilab.org/17873 – Thomas Watnedal 19 September 2014 в 12:52
  • 5
    Да, я обнаружил, что mkstemp() возвращает 2-кортеж и (fh, abs_path) = fh, abs_path, я не знал этого, когда задавал вопрос. – Wicelo 20 September 2014 в 04:31

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

import os
os.system('sed -i \'s/foo/bar/\' '+file_path)
0
ответ дан 7kemZmani 26 August 2018 в 14:25
поделиться

Самый короткий путь, вероятно, будет заключаться в использовании fileinput module . Например, следующее добавляет номера строк в файл, на месте:

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:], без необходимости явно перебирать их. Начиная с Python 3.2, он также предоставляет удобный менеджер контекста для использования в инструкции with.


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

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

  1. Файл не слишком большой, и вы можете просто прочитать его целиком в памяти. Затем закройте файл, откройте его в режиме записи и напишите измененное содержимое назад.
  2. Файл слишком велик для хранения в памяти; вы можете переместить его во временный файл и открыть его, читая его по строкам, записывая обратно в исходный файл. Обратите внимание, что для этого требуется в два раза больше хранилища.
232
ответ дан Eli Bendersky 26 August 2018 в 14:25
поделиться
  • 1
    Я знаю, что в этом есть только две строки, однако я не думаю, что код очень выразителен сам по себе. Потому что, если вы думаете в течение секунды, если вы не знаете эту функцию, есть очень мало подсказок в том, что происходит. Печать номера строки и строки - это не то же самое, что написать ее ... если вы получите мой смысл ... – chutsu 29 May 2010 в 20:12
  • 2
    согласен. как можно использовать fileinput для записи в файл? – jml 24 January 2011 в 05:50
  • 3
    Этот файл DOES записывается в файл. Он перенаправляет stdout в файл. Взгляните на документы – brice 24 August 2011 в 17:17
  • 4
    Ключевым битом здесь является запятая в конце оператора печати: она приостанавливает оператор печати, добавляя еще одну новую строку (поскольку в строке уже есть одна). Это не совсем очевидно, хотя (вот почему Python 3 изменил этот синтаксис, к счастью). – VPeric 21 October 2011 в 15:24
  • 5
    Обратите внимание, что это не работает, если вы предоставили открывающий крючок для файла, например. когда вы пытаетесь читать / записывать кодированные файлы UTF-16. – bompf 1 July 2013 в 13:19

Используя ответ 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()
1
ответ дан Emmanuel 26 August 2018 в 14:25
поделиться
  • 1
    Вы должны скомпилировать regex OUTSIDE для цикла for, иначе это производительность – Axel 4 February 2016 в 18:49

Более питоновским способом будет использование контекстных менеджеров, таких как код ниже:

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)

Здесь вы можете найти полный фрагмент здесь .

7
ответ дан formatkaka 26 August 2018 в 14:25
поделиться

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

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"),
52
ответ дан Gringo Suave 26 August 2018 в 14:25
поделиться
  • 1
    +1. Также, если вы получили RuntimeError: input () уже активны, вызовите файл file.put () – geographika 18 November 2011 в 11:24
  • 2
    Обратите внимание, что files должна быть строкой, содержащей имя файла, не файл-объект . – atomh33ls 30 August 2013 в 11:00
  • 3
    print добавляет новую строку, которая уже может быть там. чтобы избежать этого, добавьте .rstrip () в конце ваших замещений – Guillaume Gendre 21 December 2014 в 15:09
  • 4
    Вместо этого используйте файлы arg в input (), это может быть fileinput.input (inplace = 1) и вызвать скрипт как & gt; python replace.py myfiles * .txt – chespinoza 24 February 2017 в 18:45

Как предлагает lassevk, напишите новый файл, как вы идете, вот пример кода:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()
11
ответ дан hamishmcn 26 August 2018 в 14:25
поделиться

Расширение ответа @ 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)
3
ответ дан igniteflow 26 August 2018 в 14:25
поделиться
  • 1
    Будет ли сохранено разрешение старого файла в новом файле? – Bidyut 22 August 2017 в 10:27

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

3
ответ дан Lasse Vågsæther Karlsen 26 August 2018 в 14:25
поделиться

, если вы удалите отступ внизу, он будет искать и заменять в нескольких строках. См. Ниже, например.

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)
0
ответ дан Rowan Thorpe 26 August 2018 в 14:25
поделиться
  • 1
    Форматирование этого кода Python выглядит не совсем корректно ... (я пытался исправить, но не был уверен, что было предназначено) – Andy Hayden 30 September 2012 в 19:18

Если вам нужна общая функция, которая заменяет текст любой другим текстом, это, вероятно, лучший способ, особенно если вы поклонник регулярных выражений:

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 )
10
ответ дан starryknight64 26 August 2018 в 14:25
поделиться

Вот еще один пример, который был протестирован и будет соответствовать поиску и 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!$","Goodbye\sWorld.")
65
ответ дан the Tin Man 26 August 2018 в 14:25
поделиться
  • 1
    Пример использования обеспечивает регулярное выражение, но ни searchExp in line, ни line.replace не являются операциями регулярного выражения. Разумеется, использование примера неверно. – kojiro 14 November 2011 в 20:18
  • 2
    Вместо if searchExp in line: line = line.replace(searchExp, replaceExpr) вы можете просто написать line = line.replace(searchExp, replaceExpr). Исключение не генерируется, строка остается неизменной. – David Wallace 15 November 2017 в 17:07
  • 3
    И отлично работал для меня. Я столкнулся с рядом других примеров, которые выглядели очень похоже на это, но трюк был в использовании sys.stdout.write(line). Еще раз спасибо! – Sage 16 January 2018 в 18:23

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

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

Также 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()
20
ответ дан Thijs 26 August 2018 в 14:25
поделиться
  • 1
    Вы можете использовать атрибуты rb и wb при открытии файлов, так как это сохранит исходные окончания строк – Nux 1 June 2016 в 14:35
  • 2
    В Python 3 вы не можете использовать 'wb' и 'rb' с 're'. Это даст ошибку «TypeError: не может использовать шаблон строки для объекта с байтом»). – user 24 October 2017 в 13:22
Другие вопросы по тегам:

Похожие вопросы: