stdio's remove () не всегда удаляет вовремя - PullRequest
2 голосов
/ 13 апреля 2010

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

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

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 (которые выполняются в 3 различных функциях), работают отлично.

// 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");

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

Ответы [ 3 ]

3 голосов
/ 13 апреля 2010

Так внезапно, после прочтения Скоттом упоминания о разрешениях, я подумал о «Отказано в доступе» и применил немного Google. Оказалось, что это довольно распространенная, хотя и неясная ошибка. Каф был прав, это было в другом куске кода. А именно, я забыл закрыть тот же файл в функции, предназначенной для отображения содержимого. Поскольку я не отслеживал эту конкретную деталь, она оказалась случайной.

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

1 голос
/ 13 апреля 2010

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

rename("IndiceNecesidades.old", "IndiceNecesidades.older");
remove("IndiceNecesidades.older");
rename("IndiceNecesidades.dat", "IndiceNecesidades.old");
1 голос
/ 13 апреля 2010

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

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

, который должен выдавать сообщение об ошибке, указывающее, что не удалось.

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

man rename ()

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

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

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

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

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

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

поэтому следующим шагом будет проверка наличия у вас разрешений для директории с содержимым

...