Есть ли обычная причина использовать open() вместо fopen()?
Вопрос
Я делаю небольшой проект на C после довольно долгого перерыва.Они включают в себя некоторую обработку файлов.В различной документации я заметил, что есть функции, которые возвращают FILE *
дескрипторы и другие, которые возвращают дескрипторы (маленькое целое число).Оба набора функций предлагают одни и те же базовые услуги, которые мне нужны, поэтому не имеет значения, чем я пользуюсь.
Но мне любопытна мудрость коллекции:лучше ли использовать fopen()
и друзья, или open()
и друзья?
Редактировать Поскольку кто-то упомянул буферизованные и небуферизованные устройства и доступ к устройствам, я должен добавить, что одна часть этого небольшого проекта будет писать драйвер файловой системы пользовательского пространства под FUSE.Таким образом, доступ на уровне файла может быть легко реализован на устройстве (например,компакт-диск или дисковод SCSI), как в «файле» (т.е.изображение).
Решение
Возражение, что «fopen» является переносимым, а «open» — нет, является фиктивным.
fopen — часть libc, open — системный вызов POSIX.
Каждый из них так же портативен, как и место, откуда он родом.
Ввод-вывод в файлы с функцией fopen (вы должны предположить, что это может быть, и для практических целей это так) буферизуется libc, файловые дескрипторы, используемые open(), не буферизуются libc (они вполне могут быть и обычно так и делают). буферизуется в файловой системе, но не все, что вы открываете(), является файлом в файловой системе.
Какой смысл открывать, например, узел устройства, например, /dev/sg0 или /dev/tty0...Чем ты планируешь заняться?Вы собираетесь выполнить ioctl для ФАЙЛА*?Удачи с этим.
Возможно, вы хотите открыть его с помощью некоторых флагов, таких как O_DIRECT — использование fopen() не имеет смысла.
Другие советы
Лучше использовать open(), если вы придерживаетесь unix-подобных систем и вам может потребоваться:
- Получите более детальный контроль над битами разрешений Unix при создании файлов.
- Используйте функции более низкого уровня, такие как чтение/запись/mmap, в отличие от функций ввода-вывода буферизованного потока C.
- Используйте планирование ввода-вывода на основе файлового дескриптора (fd) (опрос, выбор и т. д.). Конечно, вы можете получить fd из FILE * с помощью fileno(), но необходимо соблюдать осторожность, чтобы не смешивать потоковые функции на основе FILE * с функциями на основе fd. .
- Откройте любое специальное устройство (не обычный файл)
Для максимальной переносимости лучше использовать fopen/fread/fwrite, поскольку это стандартные функции C, а функции, упомянутые выше, таковыми не являются.
fopen работает на более высоком уровне, чем open....fopen возвращает указатель на поток FILE, который похож на абстракцию потока, которую вы читаете в C++.
open возвращает файловый дескриптор открытого файла...Он не предоставляет вам абстракцию потока, и вы сами несете ответственность за обработку битов и байтов...Это более низкий уровень по сравнению с fopen.
Потоки Stdio буферизуются, а файловые дескрипторы open() — нет.Зависит от того, что вам нужно.Вы также можете создать одно из другого:
int fileno (FILE * поток) возвращает файловый дескриптор для FILE *, FILE * fdopen(int fildes, const char * mode) создает FILE * из файлового дескриптора.
Будьте осторожны при смешивании буферизованного и небуферизованного ввода-вывода, поскольку вы потеряете содержимое буфера, если не очистите его с помощью fflush().
Да.Когда вам нужен дескриптор низкого уровня.
В операционных системах UNIX обычно можно обмениваться дескрипторами файлов и сокетами.
Кроме того, низкоуровневые дескрипторы обеспечивают лучшую совместимость с ABI, чем указатели FILE.
обычно вам следует использовать стандартную библиотеку (fopen).Однако бывают случаи, когда вам придется использовать open напрямую.
Один из примеров, который приходит на ум, — это обход ошибки в старой версии Solaris, из-за которой fopen завершался сбоем после открытия 256 файлов.Это произошло потому, что они ошибочно использовали беззнаковый символ для поля fd в своей реализации структуры FILE вместо int.Но это был очень специфический случай.
читать() & писать() используйте небуферизованный ввод-вывод.(ФД:целочисленный дескриптор файла)
читать() & fwrite() используйте буферизованный ввод-вывод.(ФАЙЛ* указатель структуры)
Двоичные данные, записываемые в канал с помощью писать() может нет уметь читать двоичные данные с помощью читать(), из-за выравнивания байтов, размеров переменных и т. д.Это дерьмовая стрельба.
В большинстве низкоуровневых кодов драйверов устройств используются небуферизованные вызовы ввода-вывода.
Большинство операций ввода-вывода уровня приложения используют буферизацию.
Использование ФАЙЛ* и связанные с ним функции в порядке на машине:Но переносимость теряется на других архитектурах при чтении и написании бинарных данных.FWRITE () - буферизованный ввод -вывод и может привести к ненадежным результатам, если написано для 64 -битной архитектуры, и запустить 32 -битную;или (Windows/Linux).Большинство ОС имеют макросы совместимости в собственном коде, чтобы предотвратить это.
Для низкоуровневой переносимости двоичного ввода-вывода читать() и писать() Гарантируйте те же бинарные чтения и записывает при составлении в различных архитектурах.Основная вещь - выбрать так или иной путь и быть последовательным в этом, по всему двоичному набору.
<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.
Они обеспечивают мелкозернистый контроль над чтением и письменными байтами (рекомендуется для специальных устройств и FIFO (трубы)).
Итак, еще раз: используйте то, что вам нужно, но сохраняйте последовательность в своих идиомах и интерфейсах.Если большая часть вашей кодовой базы использует один режим, используйте это тоже, если нет реальной причины не делать этого.Оба набора функций библиотеки ввода/вывода чрезвычайно надежны и используются миллионы раз в день.
примечание-- Если вы взаимодействуете CI/O с другим языком (Perl, Python, Java, C#, Lua ...) проверить То, что разработчики этих языков рекомендуют, прежде чем написать свой код C и сохранить себе некоторые проблемы.
fopen и его родственники буферизуются.открытие, чтение и запись не буферизуются.Ваше приложение может волноваться, а может и не волноваться.
fprintf и scanf имеют более богатый API, который позволяет читать и записывать форматированные текстовые файлы.чтение и запись используют фундаментальные массивы байтов.Преобразования и форматирование должны выполняться вручную.
Разница между файловыми дескрипторами и (FILE *) на самом деле несущественна.
Рэнди