Как правильно сохранять файлы?
3240 сообщений
#17 лет назад
Допустим, есть какой-либо файл, например, конфиг.Нужно прочитать его, изменить какое-то значение, и опять записать.
Казалось бы, что может быть проще?
Выполняя аудит кода для различных команд, я обратил внимание, что практически в каждом проекте неправильно делается сохранение файлов.
И для меня оказалось откровением, что для подавляющего большинства даже неплохих программистов это знание оказалось закрытым...
Чтобы не повторяться многократно, хочу поделиться этим сокровенным знанием. Кстати, это применимо к всем языкам программирования.
1. Создаете временный файл, при этом обрабатываете все возможные при этом исключительные ситуации.
2. Запись делаете в этот временный файл, при этом также обрабатываете все возможные при записи исключительные ситуации.
3. Закрываете временный файл, и помните, что при закрытии тоже могут быть исключительные ситуации (так как делается flush буфера и ряд операций в файловой системе).
4. Если на предыдущих шагах все в порядке, переименовываете временный файл, присваивая ему имя существующего файла, при этом старый файл удаляется, и временный файл его заменяет. В этом случае также нужно перехватывать и обрабатывать все возможные исключительные ситуации.
Шаги 1-3 могут быть объединены в один шаг, если используется специальная функция или метод для создания и сохранения файла целиком, одной операцией.
Если так не делать, то малейшая проблема типа недостатка места на диске может привести к потере данных (например, если нет места на диске, то при обычной записи в файл напрямую будет создан файл нулевой длины на месте старого, и дальнейшая запись производиться не будет). И таких проблем возможна масса, например, компьютер может перезагрузиться в процессе сохранения, в этом случае также запись не будет выполнена, и файл окажется запорченным, и т.д.
P.S. Для тех, кто это все знает, прошу меня извинить за этот пост, но больно уж накипело...
49 сообщений
#17 лет назад
Все более-менее нормальные IDE настраиваются на автосохранение, причем может обновляться как уже существующий, так и создаваться новый tmp файл. В форс-мажорном случае вы сможете быстро достать все данные, даже если не сохраняли файл. Также перед редактированием исходного файла многие IDE могут создавать его копию, чтобы потом можно было легко откатить все изменения.В общем, советую порыться полчаса-час в настройках, чем потом тратить время на восстановление данных и порчу своего настроения. И работайте с нормальными IDE

51 сообщение
#17 лет назад
Если у какого-то программиста, ещё автоматом пальцы не нажимают Ctrl+S или не ставят точку с запяток - то он все ещё пограмист 

Но думаю tvv совсем не это имел ввиду. Речь шла о сохранении какого-то файла из языка...
Не считая себя очень умным (а вдруг я действительно чего-то не знаю?)

Зачем же столько гемора?
Например для записи конфига, я создам отдельную функцию\метод, который весь покрою в try..catch.. что-то вроде
function saveConfig () {
try { fopen, fwrite, fclose.. } catch (Exception $e) {
echo 'Опа! Ошибочка: ', $e->getMessage(), "\n";
}
}
Имхо этим макаром, я сразу выполню условия из вашего первого пункта
" при этом обрабатываете все возможные при этом исключительные ситуации."
И потребность в остальных пунктах просто отпадает...
Конечно здесь кое-что зависит от ситуации. Например записывая многомегабайтный файл, стоит сначала проверить, хаватит ли места на его запись. А если он генерируется в процессе работы и записывается по частям, то возможен и вариант с временным файлом, и последующим переименовыванием. Но в большинстве ситуаций (как например с файлом конфига) вероятность исключения при работе с файлом стремится к нулю. Но разумеется нулем не является, на это и есть try..catch.
710 сообщений
#17 лет назад
RUSYA, ну выскочит у вас exception на fopen или fwrite, ну уйдете вы из try по ошибке -> файл то запорчен. Чего делать то будете?
3240 сообщений
#17 лет назад
Ну как же... если сделали fopen, затем начало выполняться fwrite, и тут вдруг скрипт умер (или по сигналу, или просто сервер перезагрузился), то что будет с файлом?Он получится запорченным (или нулевого размера, или записанный не полностью).
И ничего с этим сделать не получится, если использовать прямую запись в файл всегда есть этот риск.
710 сообщений
#17 лет назад
Вадим, расскажите лучше про правильное хранение данных в БД (в плане занесения/вытаскивания данных).
1895 сообщений
#17 лет назад
Если говорить уже про очень правильное решение, то надо сделать тмп копию рабочего файла в той же папке где и рабочий документ, дальше все пункты топиккастера, как и делает напр. ms word.
710 сообщений
#17 лет назад
Цитата ("MMM_Corp"):Если говорить уже про очень правильное решение, то надо сделать тмп копию рабочего файла в той же папке где и рабочий документ, дальше все пункты топиккастера, как и делает напр. ms word.
Ээээ, а зачем tmp файл еще предварительно делать?
1895 сообщений
#17 лет назад
Не всегда можно и целесообразно помещать весь файл в память сразу.
710 сообщений
#17 лет назад
Цитата ("MMM_Corp"):Не всегда можно и целесообразно помещать весь файл в память сразу.
Иииии? У вас изначальный конфиг-файл и есть ваш tmp, из которого вы производите только чтение. А пишете то в другой.
51 сообщение
#17 лет назад
Цитата:Ну как же... если сделали fopen, затем начало выполняться fwrite, и тут вдруг скрипт умер (или по сигналу, или просто сервер перезагрузился), то что будет с файлом?
Просто сервер перезагрузился, это всеравно что просто конец света. Такое бывает ооочень редко. А если по сигналу - то ничего не делать, переспросить конфиг у пользователя ещё раз. Также дефолтовые конфиги должны быть, бекапы системы.. да много чего ещё можно деть. Соглашусь, вариант и ваш не плох, и он работает, просто есть также масса других. А выбирать какой из них лучшее - нужно смотреть по ситуации.
Например в вашем варианте, также сервер может быть перезагружен, до того, как переименуется старый файл на новый?
Всёравно у пользователя потом переспрашивать, всеравно частично работоспособность системы будет утеряна, т.к. конфиг стал не актуальный, а актуальные не успели переименовать..
710 сообщений
#17 лет назад
Цитата ("RUSYA"):Соглашусь, вариант и ваш не плох, и он работает, просто есть также масса других.
Приведите еще, пожалуйста, другие примеры, так сказать в общую копилку.

Цитата ("RUSYA"):
Всёравно у пользователя потом переспрашивать, всеравно частично работоспособность системы будет утеряна, т.к. конфиг стал не актуальный, а актуальные не успели переименовать..
А проверку на два файла никак не сделать при старте? При отсутствии конфига переименовывать tmp в конфиг.
51 сообщение
#17 лет назад
Варианты я пытался привести выше..Проверка на два файла при старте - это хорошо. Но что если сервак умер в момент работы fwrite? tmp файл будет глюченым..
28 сообщений
#17 лет назад
Цитата ("SolNikolay"):Вадим, расскажите лучше про правильное хранение данных в БД (в плане занесения/вытаскивания данных).
В любой СУБД как минимум имеется механизм двух-фазной фиксации транзакций.
Вадим, по сути, предлагает аналогичный механизм применять вне СУБД.
Меня удивляет наличие возражений. Закон Мэрфи забыли (или не знали) ?