Как удалить только содержимое файла в python

У меня есть временный файл с некоторым содержимым и скрипт python, генерирующий некоторый вывод в этот файл. Я хочу, чтобы это повторялось N раз, поэтому мне нужно повторно использовать этот файл (фактически массив файлов). Я удаляю весь контент, поэтому в следующем цикле временный файл будет пуст. Для удаления контента я использую этот код:

def deleteContent(pfile):

    pfile.seek(0)
    pfile.truncate()
    pfile.seek(0) # I believe this seek is redundant

    return pfile

tempFile=deleteContent(tempFile)

Мой вопрос: есть ли другой (лучший, более короткий или безопасный) способ удалить весь контент без фактического удаления временного файла с диска?

Что-то вроде tempFile.truncateAll()?


person bartimar    schedule 15.06.2013    source источник
comment
Второй поиск действительно избыточен. Почему бы просто не создать новый временный файл?   -  person Martijn Pieters    schedule 15.06.2013
comment
Потому что для одного обычного запуска скрипта мне понадобится около 400 временных файлов вместо ~10. Поэтому я думаю, что лучше их переработать. Я ошибся?   -  person bartimar    schedule 15.06.2013
comment
Сталкивались ли вы с реальными проблемами? Я бы просто создал новые временные файлы и позволил бы Python и ОС очистить те, которые я закрыл.   -  person Martijn Pieters    schedule 15.06.2013
comment
На самом деле удаление и закрытие их было бы большим количеством запутанных строк кода. У меня нет проблем с моим решением, мне просто нужно знать больше способов, как это сделать, и проверить производительность (при этом упрощая код).   -  person bartimar    schedule 15.06.2013
comment
Если вы используете модуль tempfile, вам не нужно удалять что угодно. Используйте временный файл в качестве диспетчера контекста (with ...), и он также будет закрыт автоматически.   -  person Martijn Pieters    schedule 15.06.2013


Ответы (4)


Как удалить только содержимое файла в python

Существует несколько способов установить логический размер файла равным 0, в зависимости от того, как вы получаете доступ к этому файлу:

Чтобы очистить открытый файл:

def deleteContent(pfile):
    pfile.seek(0)
    pfile.truncate()

Чтобы очистить открытый файл, дескриптор которого известен:

def deleteContent(fd):
    os.ftruncate(fd, 0)
    os.lseek(fd, 0, os.SEEK_SET)

Чтобы очистить закрытый файл (чье имя известно)

def deleteContent(fName):
    with open(fName, "w"):
        pass



У меня есть временный файл с некоторым содержимым [...] Мне нужно повторно использовать этот файл

При этом в общем случае скорее всего неэффективно и нежелательно повторно использовать временный файл. Если у вас нет особых потребностей, вам следует подумать об использовании tempfile.TemporaryFile и менеджера контекста для почти прозрачного создания/использования/удаления ваших временных файлов:

import tempfile

with tempfile.TemporaryFile() as temp:
     # do whatever you want with `temp`

# <- `tempfile` guarantees the file being both closed *and* deleted
#     on exit of the context manager
person Sylvain Leroux    schedule 15.06.2013
comment
pfile.truncate(0) не сбрасывает указатель файла, поэтому вам нужно будет сделать pfile.seek(0) в любом случае. То же самое относится и к os.ftruncate(). FWIW, вы можете получить дескриптор файла из pfile.fileno(), поэтому os.ftruncate(pfile.fileno(), 0) будет работать, но вам все равно нужно будет сделать pfile.seek(0) после этого. - person Aya; 15.06.2013
comment
Из docs.python.org/2/library/stdtypes.html#file .truncate Note that if a specified size exceeds the file’s current size, the result is platform-dependent: possibilities include that the file may remain unchanged, increase to the specified size as if zero-filled, or increase to the specified size with undefined new content. Вот почему я этого не делал. - person bartimar; 15.06.2013
comment
Я действительно смотрел на этот документ прямо сейчас. Я понимаю, что указатель файла может оставаться на своем месте, если он все еще действителен (т.е. указывает перед новым логическим концом файла). Но что же нам обрезать файл до текущей позиции? Так что я сделал тест. В Linux truncate(0) не перемещает текущую позицию, как сообщает ftell()--, но последующая запись выполняется в начале файла, как и ожидалось. - person Sylvain Leroux; 15.06.2013
comment
@bartimar В конечном счете, они просто вызовут один из truncate(2) или ftruncate(2), поэтому справочная страница пожалуй, лучшая документация. - person Aya; 15.06.2013
comment
@SylvainLeroux Не для меня это не так. f = open('foo', 'wb'); f.write('foo'); f.truncate(0); f.write('foo'); print f.tell() печатает 6. - person Aya; 15.06.2013
comment
@Aya Извините, я недостаточно ясно выразился: после truncate(0) tell() сообщает о позиции после конца файла. Но если вы flush или close просматриваете свой файл и проверяете его извне, вы увидите, что новый контент записывается в начале файла. Как и ожидалось. f = open('foo', 'wb'); f.write('Hello'); f.truncate(0); print f.tell(); f.write('Bonjour'); print f.tell(); fclose() сообщит 5 и 12 соответственно. at tell() -- но содержимое файла будет bonjour. - person Sylvain Leroux; 15.06.2013
comment
@SylvainLeroux Для меня контент "\x00\x00\x00\x00\x00Bonjour". Сделайте xxd на foo, чтобы проверить. Таким образом, вы фактически создаете разреженный файл. - person Aya; 15.06.2013
comment
@Aya Это забавно, потому что у меня нет разреженных байтов в начале моего файла (Python 2.6, файловая система Ext3)?!? Но поведение, которое вы наблюдаете, больше, чем я ожидал в первую очередь. Странно, что у нас нет такого же результата... - person Sylvain Leroux; 15.06.2013
comment
@SylvainLeroux Это может зависеть от ОС. Я использую Ubuntu 13.04, Python 2.7.4 и ext4. - person Aya; 15.06.2013
comment
@Ая А-а-а! У меня есть разреженные байты, если я провожу тест на файле, открытом как двоичный файл wb. Но нет, если я открою файл как текст wt. Не могли бы вы это подтвердить? - person Sylvain Leroux; 15.06.2013
comment
@SylvainLeroux В любом случае я получаю ведущие NULL. Linux все равно игнорирует флаг b. Из fopen(3)... Строка режима также может включать букву "b" как последний символ или как символ между символами в любой из двухсимвольных строк, описанных выше. Это строго для совместимости с C89 и не имеет никакого эффекта; 'b' игнорируется во всех системах, соответствующих POSIX, включая Linux. - person Aya; 15.06.2013
comment
@Aya Ладно, я схожу с ума -- или действительно пора ложиться спать? В любом случае вы правы. Я не знаю, что я делал раньше, но путем тщательного повторного тестирования я получаю разреженный файл в обоих случаях. Извините, что потратил ваше время;) Я удалил усечение без поиска из своего ответа. - person Sylvain Leroux; 15.06.2013
comment
извините, что очень поздно, но нет ли способа удалить без вызова .seek(0), а также удалить содержимое с самого начала? - person Charlie Parker; 07.02.2017
comment
@CharlieParker: seek(0) не требуется, если вы ничего не записываете в файл после (когда вы хотите очистить файл непосредственно перед его закрытием); в этом случае достаточно fileobj.truncate(0). Но если вы собираетесь писать в файл после этого, необходимо указать seek, иначе вы получите непереносимое поведение (например, разреженные файлы, упомянутые в этой ветке комментариев). .seek(0), за которым следует .truncate() (или .truncate(0), чтобы сохранить вызов lseek под капотом), усекает и гарантирует, что файл ведет себя как обычный, только что открытый пустой файл. - person ShadowRanger; 13.03.2019
comment
@SylvainLeroux: Небольшое примечание: ваше решение для очистки закрытого файла (имя которого известно) создает пустой файл, если он не существует. Решение, которое не создает файл (возбуждает исключение, если он не существует) и не требует настройки файлового объекта уровня Python, — это просто def deleteContent(fName): os.truncate(fName, 0). Однако требуется Python 3.3+. - person ShadowRanger; 13.03.2019

Я думаю, что проще всего просто открыть файл в режиме записи, а затем закрыть его. Например, если ваш файл myfile.dat содержит:

"This is the original content"

Тогда вы можете просто написать:

f = open('myfile.dat', 'w')
f.close()

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

f = open('myfile.dat', 'w')
f.write('This is the new content!')
f.close()
person Peaceful    schedule 07.07.2016

Что может быть проще, чем что-то вроде этого:

import tempfile

for i in range(400):
    with tempfile.TemporaryFile() as tf:
        for j in range(1000):
            tf.write('Line {} of file {}'.format(j,i))

Это создает 400 временных файлов и записывает 1000 строк в каждый временный файл. На моей ничем не примечательной машине он выполняется менее чем за 1/2 секунды. В этом случае каждый временный файл итога создается и удаляется при открытии и закрытии диспетчера контекста. Это быстро, безопасно и кроссплатформенно.

Использование tempfile намного лучше, чем попытки изобрести его заново.

person dawg    schedule 15.06.2013
comment
Я думаю, что seek(0) и truncate() без цикла for на самом деле проще, лучше (возможно, быстрее) и приятнее для ОС/питона :) Я боялся, что кто-то поймается на повторном использовании/переработке... Тем не менее мой вопрос тот же, так что это на самом деле не ответ. - person bartimar; 15.06.2013
comment
Вы проверяли это предположение? Вы приурочили это, чтобы видеть? - person dawg; 15.06.2013

Ты можешь сделать это:

def deleteContent(pfile):
    fn=pfile.name 
    pfile.close()
    return open(fn,'w')
person the wolf    schedule 17.06.2013