Python чтение и запись в файл добавляет неожиданные символы при прерывании сценария

Вот моя проблема, у меня есть скрипт, который состоит из многих шагов, в основном он открывает файл, читает его и после прочтения записывает обратно в файл. Все хорошо, когда сценарий завершается. Проблемы возникают, когда возникает своего рода исключение или сценарий прерывается. Я открываю файл в режиме «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'

Опубликуйте минимальный воспроизводимый пример

wjandrea 07.04.2019 22:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
278
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Хорошие идеи о том, как этого избежать. А почему это происходит?

George Mogilevsky 07.04.2019 23:04

Файловые системы — сложные звери. IO в файловые системы из языков высокого уровня тем более. Мое лучшее предположение - "что-то идет не по той трубе", так сказать, возможно, что-то на уровне реализации в самом Python. Но это предположение.

quiet_laika 07.04.2019 23:06
Ответ принят как подходящий

Вы видите эффект буферизации.

Вы можете уменьшить эффект, добавив вызов румянец:

    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».

George Mogilevsky 07.04.2019 23:33

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

str_to_oc = “myVar=“+str(13)+”#”

Другие вопросы по теме