Файл записи перекрывается и эквивалентен fwrite

#c #file #io

#c #файл #io

Вопрос:

В Windows WriteFile() функция имеет вызываемый параметр lpOverlapped , который позволяет указать смещение, с которым выполняется запись в файл.

Мне было интересно, есть ли fwrite() кросс-платформенный эквивалент этого?

Я вижу, что если файл открыт с rb флагом, я мог бы использовать fseek() для записи в определенное смещение. Мой вопрос — будет ли этот подход эквивалентен overlapped WriteFile() и приведет ли он к одинаковому поведению на всех платформах?

Предыстория

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

Комментарии:

1. Используйте fseek перед fwrite .

2. fwrite() это C stdio, а не C I / O.

Ответ №1:

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

 ssize_t rc = pwrite(file_handle, data_ptr, data_size, destination_offset);
 

Комментарии:

1. Примечание: pwrite ближе к write(2) , чем fwrite(3) . Но тогда, как WriteFile и; pwrite , write , и WriteFile все это в основном тонкие оболочки над системными вызовами, где fwrite более высокий уровень с буферизацией пользовательского пространства.

2. Я думаю, что это то, о чем на самом деле просит OP, поскольку он уже использует специфичный для Windows WriteFile вызов.

3. Яр. Я хотел прояснить различие, ничего плохого в ответе (я проголосовал).

Ответ №2:

Я думаю, вы путаете «перекрытие» и «перезапись» / «смещение». Я не изучал особенности того, почему Microsoft явно указывает, что перекрывающиеся записи включают параметр для смещения (я думаю, это имеет смысл, как я описываю ниже). В общем, когда Microsoft говорит о «перекрытом» вводе-выводе, они говорят о том, как синхронизировать такие события, как начало записи файла, получение уведомления о завершении записи и начало другой записи в файл, которая может перекрываться или не перекрываться с предыдущей записью. В этом последнем случае под перекрытием я подразумеваю то, что, по вашему мнению, означает перекрытие, то есть перекрытие содержимого файла. Принимая во внимание, что Microsoft означает, что запись файла перекрывается по времени с запуском вашего потока или нет. Обратите внимание, что это становится очень сложным, если несколько потоков могут записать один и тот же файл.

Если возможно, и, конечно, если вам нужен переносимый код, вы хотите избежать всей этой ерунды и просто выполнять простейшую запись, возможную в каждом контексте, что означает избегать оптимизаций Microsoft, таких как «overlapped IO», если вам действительно не нужна производительность. (И если вам нужна абсолютно оптимальная производительность, вы можете самостоятельно кэшировать файл и управлять перекрытиями, а затем записать его один раз от начала до конца.)

Ответ №3:

Хотя pwrite это, вероятно, лучшее решение, есть альтернатива, которая придерживается stdio функций. К сожалению, чтобы сделать его потокобезопасным, вы используете нестандартный « stdio » для прямого управления FILE* внутренней блокировкой, а имена не переносимы. В частности, POSIX определяет один набор имен «принять / снять блокировку файла», а Windows определяет другой набор ( _lock_file / _unlock_file ).

Тем не менее, вы могли бы использовать эти полупереносимые конструкции для использования stdio функций для обеспечения отсутствия конфликтов буферизации ( pwrite to fileno(some_FILE_star) может вызвать проблемы, если FILE* буфер перекрывает pwrite местоположение, поскольку pwrite не исправит буфер):

 // Error checking omitted; you should actually check returns in real code
size_t pfwrite(const void *ptr, size_t size, size_t n,
               size_t offset, FILE *stream) {
    // Take FILE*'s lock and hold it for entire transaction
    flockfile(stream);  // _lock_file on Windows

    // Record position
    long origpos = ftell(stream);

    // Seek to desired offset and write
    fseek(stream, offset, SEEK_SET); // Possibly offset * size, not just offset?
    size_t written = fwrite(ptr, size, n, stream);

    // Seek back to original position
    fseek(stream, origpos, SEEK_SET);

    // Release FILE*'s lock now that transaction complete
    funlockfile(stream); // _unlock_file on Windows
    return written;
}
 

Комментарии:

1. Спасибо и за это. Мне не нужна потокобезопасность для этого приложения, но это полезно знать.