Как изменить текстовый файл?

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


person Community    schedule 24.09.2008    source источник
comment
Вы можете обратиться к этому ответу Алекса Мартелли.   -  person Alok    schedule 02.04.2014
comment
stackoverflow.com/a/4358169/538284   -  person Omid Raha    schedule 02.04.2014
comment
Возможный дубликат Запись в самой верхней строке CSV-файл в Python   -  person Ani Menon    schedule 15.06.2016
comment
@Ani другой пост является дубликатом Вставка строки в указанную позицию текстового файла в любом случае и, безусловно, здесь есть четкие составленные ответы, почему бы не добавить свой ответ здесь, а не наоборот? Принятый ответ не является требованием для хорошего вопроса.   -  person Bhargav Rao    schedule 15.06.2016
comment
@BhargavRao Голосование отозвано. Я должен был найти этот дубликат, хотя!   -  person Ani Menon    schedule 15.06.2016
comment
См. также: stackoverflow.com/a/1325927/2276527   -  person dreftymac    schedule 28.09.2020


Ответы (8)


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

Это дело операционной системы, а не Python. Он одинаков во всех языках.

Что я обычно делаю, так это читаю файл, вношу изменения и записываю его в новый файл с именем myfile.txt.tmp или что-то в этом роде. Это лучше, чем чтение всего файла в память, потому что файл может быть слишком большим для этого. После создания временного файла я переименовываю его так же, как исходный файл.

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

person Adam Pierce    schedule 24.09.2008
comment
Делают ли инструменты Unix, такие как awk/sed, что-то подобное в своем коде? - person Manish Gill; 22.03.2013
comment
Неправда, что это одинаково во всех языках. В ActionScript: fileStream.openAsync(имя файла,FileMode.UPDATE); Затем я могу перейти в любое место в файле и изменить что-либо. - person AndrewBenjamin; 11.07.2014
comment
@AndrewBenjamin Знаете ли вы, какие системные вызовы делает ActionScript? Есть ли вероятность, что openAsync прочитает файл и запишет новый после вызова? - person AlexLordThorsen; 03.12.2014
comment
@Rawrgulmuffins Нет. Однако я знаю, что он не считывает весь файл в память, так как я использовал его для обработки файлов размером в несколько ГБ. Я подозреваю, что это то же самое, что писать с помощью C# streamwriter. Я рассматриваю Python как инструмент для быстрого выполнения мелких задач, а не как крупномасштабную разработку и манипулирование файлами. - person AndrewBenjamin; 27.03.2015
comment
@AndrewBenjamin, пользователь не спрашивает о поиске в файле и его изменении (каждый известный мне язык может это сделать); он спрашивает о вставке текста, что отличается от простого изменения/перезаписи того, что уже есть в файле. Возможно, в практическом применении это отличается, но я ничего не могу найти в API ActionScript указывает, что в этом отношении его поведение отличается от любого другого языка. - person eestrada; 22.07.2015
comment
Может быть, я неправильно понял. Я думал, что это одно и то же — FileStream.writeUTF - person AndrewBenjamin; 22.07.2015

Зависит от того, что вы хотите сделать. Чтобы добавить, вы можете открыть его с помощью «a»:

 with open("foo.txt", "a") as f:
     f.write("new line\n")

Если вы хотите подготовить что-то, вы должны сначала прочитать из файла:

with open("foo.txt", "r+") as f:
     old = f.read() # read everything in the file
     f.seek(0) # rewind
     f.write("new line\n" + old) # write the new line before
person Armin Ronacher    schedule 24.09.2008
comment
Небольшое дополнение: чтобы использовать оператор with в Python 2.5, вам нужно добавить from future import with_statement. Кроме того, открытие файлов с помощью оператора with определенно более читабельно и менее подвержено ошибкам, чем ручное закрытие. - person Alexander Kojevnikov; 24.09.2008
comment
Вы можете рассмотреть вспомогательную библиотеку fileinput с прекрасной обработкой грязной процедуры открытия/чтения/изменения/записи/замены при использовании inline=True arg. Пример здесь: stackoverflow.com/a/2363893/47390 - person mikegreenberg; 02.02.2012
comment
Только не забудьте закрыть файл. f.Close() - person D.Rosado; 02.05.2012
comment
Это не тот стиль, который я использую, Д.Росадо, но при использовании стиля with я не думаю, что вам нужно закрывать вручную. with отслеживает ресурс, который он создает. - person Chris; 14.05.2012
comment
Вам не нужно закрывать файл вручную. В этом весь смысл использования with here. (Ну, на самом деле, Python делает это, как только файловый объект очищается сборщиком мусора, что в CPython происходит, когда связанное с ним имя выходит за рамки... но другие реализации этого не делают, и CPython может перестать делать это когда-нибудь. , поэтому с рекомендуется) - person Jürgen A. Erhard; 22.06.2013

Модуль fileinput стандартной библиотеки Python будет переписан файл на месте, если вы используете параметр inplace=1:

import sys
import fileinput

# replace all occurrences of 'sit' with 'SIT' and insert a line after the 5th
for i, line in enumerate(fileinput.input('lorem_ipsum.txt', inplace=1)):
    sys.stdout.write(line.replace('sit', 'SIT'))  # replace 'sit' and write
    if i == 4: sys.stdout.write('\n')  # write a blank line after the 5th line
person Dave    schedule 28.11.2009
comment
Как это должно работать в python3? Я только что перенес приложение с таким кодом с python на python3, и я просто не мог заставить его работать правильно. Переменная «строка» имеет тип байтов, я попытался декодировать ее в юникод, а затем изменить, а затем снова закодировать в байты, но она просто не работала правильно. Это вызвало какое-то исключение, которое я не могу вспомнить навскидку. Успешно ли люди используют fileinput inplace=1 в python3? - person robru; 21.02.2015
comment
@Robru: вот код Python 3 - person jfs; 19.12.2016
comment
Но это не проблема, потому что вы сначала протестировали его на неважном файле, верно? - person Paula Livingstone; 18.11.2017

Перезапись файла на месте часто выполняется путем сохранения старой копии с измененным именем. Люди Unix добавляют ~, чтобы отметить старый. Разработчики Windows делают все, что угодно — добавляют .bak или .old — или полностью переименовывают файл, или ставят ~ перед именем.

import shutil
shutil.move( afile, afile+"~" )

destination= open( aFile, "w" )
source= open( aFile+"~", "r" )
for line in source:
    destination.write( line )
    if <some condition>:
        destination.write( >some additional line> + "\n" )
source.close()
destination.close()

Вместо shutil можно использовать следующее.

import os
os.rename( aFile, aFile+"~" )
person S.Lott    schedule 24.09.2008
comment
Выглядит неплохо. Хотите знать, лучше ли .readlines(), чем итерация исходного кода? - person bozdoz; 10.04.2013
comment
@bozdoz: итерация лучше, так как readlines читает весь файл. Не подходит для больших файлов. Конечно, это предполагает, что вы можете делать свои модификации таким локализованным способом. Иногда вы не можете, или ваш код становится намного сложнее. - person Jürgen A. Erhard; 22.06.2013
comment
@S.Lott: os.rename(aFile, aFile + "~") изменит имя исходного файла, не создавая копию. - person Patapoom; 12.03.2020

Модуль Python mmap позволит вам вставить в файл. В следующем примере показано, как это можно сделать в Unix (MMAP для Windows может отличаться). Обратите внимание, что это не обрабатывает все условия ошибки, и вы можете повредить или потерять исходный файл. Кроме того, это не будет обрабатывать строки Unicode.

import os
from mmap import mmap

def insert(filename, str, pos):
    if len(str) < 1:
        # nothing to insert
        return

    f = open(filename, 'r+')
    m = mmap(f.fileno(), os.path.getsize(filename))
    origSize = m.size()

    # or this could be an error
    if pos > origSize:
        pos = origSize
    elif pos < 0:
        pos = 0

    m.resize(origSize + len(str))
    m[pos+len(str):] = m[pos:origSize]
    m[pos:pos+len(str)] = str
    m.close()
    f.close()

Это также возможно сделать без mmap с файлами, открытыми в режиме «r+», но это менее удобно и менее эффективно, так как вам придется читать и временно сохранять содержимое файла из позиции вставки в EOF, что может быть огромным.

person mhawke    schedule 25.09.2008

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

Если вы имеете дело с небольшим файлом или у вас нет проблем с памятью, это может помочь:

Вариант 1) Считайте весь файл в память, выполните подстановку регулярных выражений для всей или части строки и замените ее этой строкой плюс лишняя строка. Вам нужно будет убедиться, что «средняя строка» уникальна в файле, или если у вас есть временные метки в каждой строке, это должно быть довольно надежным.

# open file with r+b (allow write and binary mode)
f = open("file.log", 'r+b')   
# read entire content of file into memory
f_content = f.read()
# basically match middle line and replace it with itself and the extra line
f_content = re.sub(r'(middle line)', r'\1\nnew line', f_content)
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(f_content)
# close file
f.close()

Вариант 2) Определите среднюю линию и замените ее этой линией плюс дополнительной строкой.

# open file with r+b (allow write and binary mode)
f = open("file.log" , 'r+b')   
# get array of lines
f_content = f.readlines()
# get middle line
middle_line = len(f_content)/2
# overwrite middle line
f_content[middle_line] += "\nnew line"
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(''.join(f_content))
# close file
f.close()
person Maxime R.    schedule 19.11.2012

Написал небольшой класс, чтобы сделать это чисто.

import tempfile

class FileModifierError(Exception):
    pass

class FileModifier(object):

    def __init__(self, fname):
        self.__write_dict = {}
        self.__filename = fname
        self.__tempfile = tempfile.TemporaryFile()
        with open(fname, 'rb') as fp:
            for line in fp:
                self.__tempfile.write(line)
        self.__tempfile.seek(0)

    def write(self, s, line_number = 'END'):
        if line_number != 'END' and not isinstance(line_number, (int, float)):
            raise FileModifierError("Line number %s is not a valid number" % line_number)
        try:
            self.__write_dict[line_number].append(s)
        except KeyError:
            self.__write_dict[line_number] = [s]

    def writeline(self, s, line_number = 'END'):
        self.write('%s\n' % s, line_number)

    def writelines(self, s, line_number = 'END'):
        for ln in s:
            self.writeline(s, line_number)

    def __popline(self, index, fp):
        try:
            ilines = self.__write_dict.pop(index)
            for line in ilines:
                fp.write(line)
        except KeyError:
            pass

    def close(self):
        self.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        with open(self.__filename,'w') as fp:
            for index, line in enumerate(self.__tempfile.readlines()):
                self.__popline(index, fp)
                fp.write(line)
            for index in sorted(self.__write_dict):
                for line in self.__write_dict[index]:
                    fp.write(line)
        self.__tempfile.close()

Затем вы можете использовать его следующим образом:

with FileModifier(filename) as fp:
    fp.writeline("String 1", 0)
    fp.writeline("String 2", 20)
    fp.writeline("String 3")  # To write at the end of the file
person ananth krishnan    schedule 02.02.2016
comment
Это не работает лично для меня, оно добавляет текст в файл, но сначала удаляет все! - person Bret Hawker; 12.01.2019
comment
Действительно, это вообще не работает. Позор, потому что это казалось хорошей идеей. - person Mario Krušelj; 14.06.2019

Если вы знаете какой-нибудь unix, вы можете попробовать следующее:

Примечания: $ означает командную строку

Скажем, у вас есть файл my_data.txt с таким содержимым:

$ cat my_data.txt
This is a data file
with all of my data in it.

Затем с помощью модуля os вы можете использовать обычные команды sed

import os

# Identifiers used are:
my_data_file = "my_data.txt"
command = "sed -i 's/all/none/' my_data.txt"

# Execute the command
os.system(command)

Если вы не знаете о sed, проверьте это, это чрезвычайно полезно.

person G. LC    schedule 02.11.2018
comment
это вообще не питон - person DarkSuniuM; 27.05.2019