Вот моя проблема, у меня есть скрипт, который состоит из многих шагов, в основном он открывает файл, читает его и после прочтения записывает обратно в файл. Все хорошо, когда сценарий завершается. Проблемы возникают, когда возникает своего рода исключение или сценарий прерывается. Я открываю файл в режиме «r+», потому что, если я открою его в режиме «w», файл сразу становится пустым, а если скрипт прерывается, он остается пустым, а я хочу, чтобы он сохранял предыдущее значение. Ниже приведен пример, но не точный сценарий, который я запускаю. Если сценарий прерывается (или возникает исключение, даже если оно обрабатывается), значение внутри test.txt будет «myVar=13e» или «myVar=13ne». Не всегда, но часто. Почему это происходит и как этого избежать?
import time
from test import myVar
file_path = "./test.py"
with open(file_path, 'r+', encoding=‘utf-8’) as f:
# read the file content which is for example “myVar=11”
# do calculations with myVar
#str_to_oc = "myVar = "+str(row[0]) #row[0] is fetched from database, it’s ID of the record. It’s an integer
str_to_oc = “myVar=“+str(13) # I hardcoded the 13 value here instead of the database row[0]
time.sleep(3) #just adding a delay so you can interrupt easily
# write back a string “myVar=13” which is he new value of 13
f.write(str_to_oc)
Отредактировал пример кода, чтобы упростить тестирование
Еще один момент: подобные вещи могут происходить из-за кодировки по умолчанию системы, на которой работает скрипт. Решением было бы всегда явно указывать кодировку как для чтения, так и для записи с помощью чего-то вроде encoding='utf_8'
Очень наивным решением было бы просто прочитать файл в память, предполагая, что он настолько короткий, как предполагает ваш пример, и переписать содержимое файла в случае исключения. Вы также можете использовать временный файл, чтобы не затирать оригинал, а затем писать только в случае успеха.
Хорошие идеи о том, как этого избежать. А почему это происходит?
Файловые системы — сложные звери. IO в файловые системы из языков высокого уровня тем более. Мое лучшее предположение - "что-то идет не по той трубе", так сказать, возможно, что-то на уровне реализации в самом Python. Но это предположение.
Вы видите эффект буферизации.
Вы можете уменьшить эффект, добавив вызов румянец:
f.write(str_to_oc)
f.flush()
CTRL/C поступает асинхронно, так что это не исправит ситуацию полностью. Также, если вы выберете вставить/удалить, чтобы отдельные записи и общий размер файла менялись, вы будете недовольны тем, как старые + новые записи смещены.
За кулисами иногда появляется io.BufferedWriter
запрос необработанного записывать,
который превращается в системный вызов уровня ОС.
Вы говорите, что CTRL/C или фатальная трассировка стека приводят к преждевременному завершению программы.
В этом случае весь процесс интерпретатора Python завершается,
вызывая неявный close()
,
что может привести к чтению комбинации старых + новых байтов из вашего файла.
Обратите внимание, что многобайтовая кодовая точка UTF8 может охватывать блоки диска,
что может привести к несчастью.
Учитывая наблюдаемую надежность вашей программы, похоже, вам было бы хорошо оставить оригинал нетронутым пока обработка не завершится успешно:
tmp_path = file_path + '.tmp'
with open(file_path) as fin:
with open(tmp_path, 'w') as fout:
for line in fin:
# (do stuff, compute output)
fout.write(out_line + '\n')
os.rename(tmp_path, file_path) # atomic operation, all-or-nothing
Спасибо за это объяснение! На самом деле прерывание было вызвано потерей соединения с базой данных, а не Cntrl-C. Но тем не менее, у меня есть код в функции, и я вызываю функцию с помощью «try:» и «except:» внутри цикла while. Таким образом, скрипт перезапустится. Ваш ответ проливает свет на то, почему появляются эти, казалось бы, случайные значения «e» и «ne».
Просто для всех, кто заинтересован, я сделал что-то вроде взлома и добавил комментарий к строке, которую я записываю в файл.
str_to_oc = “myVar=“+str(13)+”#”
Опубликуйте минимальный воспроизводимый пример