Есть ли какая-то обычная причина использовать open () вместо fopen ()? - PullRequest
5 голосов
/ 13 февраля 2009

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

Но меня интересует мудрость коллекции: лучше ли использовать fopen() и друзей или open() и друзей?

Редактировать Поскольку кто-то упоминал буферизованные и небуферизованные устройства и обращающиеся к ним устройства, я должен добавить, что одна часть этого небольшого проекта будет писать драйвер файловой системы пользовательского пространства под FUSE. Таким образом, доступ на уровне файлов может быть как на устройстве (например, на CDROM или диске SCSI), так и на «файле» (то есть образе).

Ответы [ 7 ]

14 голосов
/ 13 февраля 2009

Лучше использовать open (), если вы придерживаетесь Unix-подобных систем и вам может понадобиться:

  • Более детальный контроль над битами разрешений Unix при создании файла.
  • Используйте функции более низкого уровня, такие как чтение / запись / mmap, в отличие от функций ввода-вывода с буферизованным потоком C.
  • Использовать планирование ввода-вывода на основе дескриптора файла (fd) (опрос, выбор и т. Д.) Конечно, вы можете получить fd из FILE * с помощью fileno (), но следует позаботиться о том, чтобы не смешивать потоковые функции на основе FILE * с функции на основе fd.
  • Открыть любое специальное устройство (не обычный файл)

Лучше использовать fopen / fread / fwrite для максимальной переносимости, поскольку это стандартные функции языка Си, а функции, о которых я упоминал выше, - нет.

6 голосов
/ 13 февраля 2009

Возражение о том, что "fopen" является переносимым, а "open" не является поддельным.

fopen является частью libc, open является системным вызовом POSIX.

Каждый такой же портативный, как и место, откуда они пришли.

Ввод / вывод в открытые файлы (вы должны предположить, что это возможно, и для практических целей) буферизируется libc, дескрипторы файлов open () 'ed не буферизируются libc (они вполне могут и обычно буферизируются в файловой системе - но не все, что вы открываете (), является файлом в файловой системе.

Какой смысл создавать, например, такой узел устройства, как, например, / dev / sg0 или / dev / tty0 ... Что вы собираетесь делать? Вы собираетесь сделать ioctl на ФАЙЛ *? Удачи с этим.

Может быть, вы хотите открыть с некоторыми флагами, такими как O_DIRECT - не имеет смысла с fopen ().

4 голосов
/ 13 февраля 2009

fopen работает на более высоком уровне, чем открытый .... fopen возвращает вам указатель на поток FILE, который похож на абстракцию потока, которую вы читаете в C ++

open возвращает вам дескриптор файла для открытого файла ... Он не предоставляет вам потоковую абстракцию, и вы сами отвечаете за обработку битов и байтов ... Это на более низком уровне по сравнению с fopen

Потоки Stdio буферизируются, а дескрипторы файлов open () - нет. Зависит от того, что вам нужно. Вы также можете создать одно из другого:

int fileno (FILE * stream) возвращает дескриптор файла для FILE *, FILE * fdopen (int fildes, const char * mode) создает FILE * из файлового дескриптора.

Будьте осторожны при смешивании буферизованного и небуферизованного ввода-вывода, поскольку вы потеряете то, что находится в вашем буфере, если не очистите его с помощью fflush ().

3 голосов
/ 13 февраля 2009

Да. Когда вам нужна ручка низкого уровня.

В операционных системах UNIX вы обычно можете обмениваться дескрипторами файлов и сокетами.

Кроме того, маркеры низкого уровня обеспечивают лучшую совместимость с ABI, чем указатели FILE.

2 голосов
/ 04 июня 2012

read () & write () использовать небуферизованный ввод / вывод. ( fd : дескриптор целочисленного файла)

fread () & fwrite () использовать буферизованный ввод / вывод. ( FILE * структурный указатель)

Двоичные данные, записанные в канал с помощью write () могут не быть в состоянии читать двоичные данные с помощью fread () , из-за выравнивания байтов, переменных размеров и т. д. Это дерьмо стрелять.

В большинстве кодов драйверов устройств низкого уровня используются небуферизованные вызовы ввода-вывода.

Большинство операций ввода-вывода прикладного уровня использует буферизацию.

Использование FILE * и связанных с ним функций все в порядке на каждой машине: но переносимость теряется на других архитектурах в чтении и записи двоичных данных. fwrite () буферизует ввод-вывод и может привести к ненадежным результатам, если написано для 64-битной архитектуры и работает на 32-битной; или (Windows / Linux). Большинство ОС имеют макросы совместимости в своем собственном коде, чтобы предотвратить это.

Для переносимости низкоуровневого двоичного ввода / вывода read () и write () гарантированно один и тот же двоичный файл читает и пишет при компиляции на разных архитектурах. Основная вещь состоит в том, чтобы выбрать один путь или другой и быть последовательным об этом, по всему бинарному набору.

<stdio.h>  // mostly FILE*  some fd input/output parameters for compatibility
             // gives you a lot of helper functions -->
List of Functions
       Function      Description
       ───────────────────────────────────────────────────────────────────
       clearerr      check and reset stream status
       fclose        close a stream
       fdopen        stream open functions //( fd argument, returns FILE*)                      feof          check and reset stream status
       ferror        check and reset stream status
       fflush        flush a stream
       fgetc         get next character or word from input stream
       fgetpos       reposition a stream
       fgets         get a line from a stream
       fileno        get file descriptor   // (FILE* argument, returns fd) 
       fopen         stream open functions
       fprintf       formatted output conversion
       fpurge        flush a stream
       fputc         output a character or word to a stream
       fputs         output a line to a stream
       fread         binary stream input/output
       freopen       stream open functions
       fscanf        input format conversion
       fseek         reposition a stream
       fsetpos       reposition a stream
       ftell         reposition a stream
       fwrite        binary stream input/output
       getc          get next character or word from input stream
       getchar       get next character or word from input stream
       gets          get a line from a stream
       getw          get next character or word from input stream
       mktemp        make temporary filename (unique)
       perror        system error messages
       printf        formatted output conversion
       putc          output a character or word to a stream
       putchar       output a character or word to a stream
       puts          output a line to a stream
       putw          output a character or word to a stream
       remove        remove directory entry
       rewind        reposition a stream
       scanf         input format conversion
       setbuf        stream buffering operations
       setbuffer     stream buffering operations
       setlinebuf    stream buffering operations
       setvbuf       stream buffering operations
       sprintf       formatted output conversion
       sscanf        input format conversion
       strerror      system error messages
       sys_errlist   system error messages
       sys_nerr      system error messages
       tempnam       temporary file routines
       tmpfile       temporary file routines
       tmpnam        temporary file routines
       ungetc        un-get character from input stream
       vfprintf      formatted output conversion
       vfscanf       input format conversion
       vprintf       formatted output conversion
       vscanf        input format conversion
       vsprintf      formatted output conversion
       vsscanf       input format conversion

Так что для базового использования я бы лично использовал вышеупомянутое, не смешивая идиомы слишком сильно.

Для сравнения

<unistd.h>   write()
             lseek()
             close()
             pipe()
<sys/types.h>
<sys/stat.h>
<fcntl.h>  open()
           creat()
           fcntl() 
all use file descriptors.

Они обеспечивают детальный контроль над чтением и записью байтов. (рекомендуется для специальных устройств и fifos (труб)).

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

note - Если вы взаимодействуете с C I / O с другим языком, (perl, python, java, c #, lua ...) посмотрите что разработчики этих языков рекомендую, прежде чем писать код на C и избавить себя от неприятностей.

2 голосов
/ 13 февраля 2009

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

Один пример, который приходит на ум, - это обойти ошибку в старой версии Solaris, которая приводила к сбою fopen после открытия 256 файлов. Это произошло потому, что они ошибочно использовали unsigned char для поля fd в своей реализации struct FILE вместо int. Но это был очень специфический случай.

0 голосов
/ 13 февраля 2009

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

fprintf и scanf имеют более богатый API, который позволяет вам читать и писать отформатированные текстовые файлы. Для чтения и записи используются фундаментальные массивы байтов. Преобразования и форматирование должны быть выполнены вручную.

Разница между файловыми дескрипторами и (FILE *) действительно несущественна.

Randy

...