2010-04-13 3 views
2

Для конкретной домашней работы я реализую базовую систему хранения данных, используя последовательные файлы по стандарту C, которые не могут загружать более 1 записи за раз. Итак, основная часть - это создание нового файла, в котором хранятся результаты того, что мы делаем с исходными записями. Переименован предыдущий файл, а новый - под рабочим именем. Код скомпилирован с MinGW 5.1.6 в Windows 7.stdio's remove() не всегда удаляется вовремя

Проблема в том, что эта конкретная версия кода (у меня почти идентичные версии этого плавающего вокруг моих функций) не всегда удаляет старый файл, поэтому переименование не выполняется, и, следовательно, сохраненные данные обрываются fopen().

FILE *archivo, *antiguo; 

remove("IndiceNecesidades.old"); // This randomly fails to work in time. 
rename("IndiceNecesidades.dat", "IndiceNecesidades.old"); // So rename() fails. 

antiguo = fopen("IndiceNecesidades.old", "rb"); 
// But apparently it still gets deleted, since this turns out null (and I never find the .old in my working folder after the program's done). 
archivo = fopen("IndiceNecesidades.dat", "wb"); // And here the data gets wiped. 

В принципе, каждый раз, когда .old ранее существует, есть шанс, что он не удален во времени для переименования(), чтобы успешно вступили в силу. Нет возможных конфликтов имен как внутри, так и снаружи.

Странно, что только с этим конкретным файлом. Идентичные фрагменты, кроме имени, измененного на Necesidades.dat (который происходит в трех разных функциях) работают отлично.

// I'm yet to see this snippet fail. 
FILE *antiguo, *archivo; 

remove("Necesidades.old"); 
rename("Necesidades.dat", "Necesidades.old"); 

antiguo = fopen("Necesidades.old", "rb"); 
archivo = fopen("Necesidades.dat", "wb"); 

Любые идеи о том, почему это произошло, и/или, как я могу гарантировать удалить() команда вступила в силу по времени переименования() выполняется? (Я думал просто использовать цикл while для принудительного вызова remove() снова, пока fopen() возвращает ненулевой указатель, но это звучит как попрошайничество для сбоя из-за переполнения ОС с помощью запросов на удаление или что-то в этом роде.)

+0

Вы проверяете возвращаемое значение удаления? И есть ли способ открыть файл, когда вы пытаетесь его удалить? – Cascabel

+0

Remove не показывает проблем (и фактически удаляет файл, просто делает это слишком поздно), переименование получается «Permission Denied» (что является причиной того, что .old все еще существует, проверяется с помощью теста fopen()). Ни в коем случае нельзя открывать файл. – Kyte

+0

Да, я подумал, что вы не упустили что-то очевидное, но это стоило выстрела. – Cascabel

ответ

3

Так внезапно, после прочтения упоминания Скотт разрешений, я думал о «Отказано в доступе» и применил некоторые Google. Turned out it's a pretty common, if obscure, error. caf был прав, это было в другом фрагменте кода. А именно, я забыл создать тот же файл в функции, предназначенной для отображения содержимого. Поскольку я не отслеживал эту конкретную деталь, это казалось случайным.

Отказ от ответственности: Еженедельные ассистенты математики делают очень мало смазанности. ¬¬

+2

Просто интересный факт: если вы используете систему POSIX, а не Windows, проблема не будет. В системе POSIX вы всегда можете удалить открытый файл, и он будет удален из каталога, хотя в то же время он будет существовать без имени, пока последний процесс не завершит его. Просто одно из отличий между семейством Windows и POSIX. –

1

Возможно, было бы неплохо проверить функцию remove() на наличие ошибок. man remove говорит, что функция возвращает 0 при успешном завершении и -1 при сбое, установив errno для регистрации ошибки. Попробуйте заменить вызов

if (remove("IndiceNecesidades.old") != 0){ 
    perror("remove(\"IndiceNecesidades.old\") failed"); 
} 

который должен сообщить об ошибке.

Кроме того, он не появляется, что удаляемый необходимому

человек Rename()

Системный вызов переименования() вызывает ссылку с именем стара, чтобы быть переименованы как новый. Если новый существует, он сначала удаляется. И старый, и новый должны быть одного типа (т. Е. Оба должны быть либо каталогов, либо не-каталогов), а должен находиться в одной файловой системе.

переименовани() гарантирует системный вызов что экземпляр нового будет всегда существовать, даже если система должна врезаться в середине операции.

Если последний компонент старой является символической ссылкой, символическая ссылка переименованы, а не файл или каталог, который он указывает.

EPERM будет возвращен, если:

[EPERM] Каталог , содержащий старый отмечен липким, и ни содержащий каталог, ни старые принадлежат эффективному пользователю ID.

[EPERM] Новый файл существует, каталог, содержащий новый отмечен липкий, и ни , содержащий каталога, ни новые, принадлежит по эффкам тивной идентификатор пользователя.

поэтому следующий шаг будет проверить у вас есть права на вмещающего каталог

+0

Я perror'd удаляю и переименовываю, только переименовывает ошибки. (Кроме того, удаление все еще продолжается.) Кстати: у вас есть ваше состояние назад. 0 - false. ;) – Kyte

+0

@Kyte: Нет, у него условия правильные. ISO C указывает, что remove() возвращает 0 при успехе и -1 при неудаче, точно так же, как он говорит. – janneb

+0

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

1

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

rename("IndiceNecesidades.old", "IndiceNecesidades.older"); 
remove("IndiceNecesidades.older"); 
rename("IndiceNecesidades.dat", "IndiceNecesidades.old"); 
Смежные вопросы